blob: 168b894da848121c35950da90cb466579f18a555 [file] [log] [blame]
Christopher Tate487529a2009-04-29 14:03:25 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Christopher Tate181fafa2009-05-14 11:12:14 -070019import android.app.ActivityManagerNative;
Christopher Tateb6787f22009-07-02 17:40:45 -070020import android.app.AlarmManager;
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -070021import android.app.AppGlobals;
Christopher Tate181fafa2009-05-14 11:12:14 -070022import android.app.IActivityManager;
23import android.app.IApplicationThread;
24import android.app.IBackupAgent;
Christopher Tateb6787f22009-07-02 17:40:45 -070025import android.app.PendingIntent;
Christopher Tate79ec80d2011-06-24 14:58:49 -070026import android.app.backup.BackupAgent;
Christopher Tate4a627c72011-04-01 14:43:32 -070027import android.app.backup.BackupDataOutput;
28import android.app.backup.FullBackup;
Jason parksa3cdaa52011-01-13 14:15:43 -060029import android.app.backup.RestoreSet;
Christopher Tate45281862010-03-05 15:46:30 -080030import android.app.backup.IBackupManager;
Christopher Tate4a627c72011-04-01 14:43:32 -070031import android.app.backup.IFullBackupRestoreObserver;
Christopher Tate45281862010-03-05 15:46:30 -080032import android.app.backup.IRestoreObserver;
33import android.app.backup.IRestoreSession;
Christopher Tate4a627c72011-04-01 14:43:32 -070034import android.content.ActivityNotFoundException;
Christopher Tate3799bc22009-05-06 16:13:56 -070035import android.content.BroadcastReceiver;
Dan Egnor87a02bc2009-06-17 02:30:10 -070036import android.content.ComponentName;
Christopher Tate487529a2009-04-29 14:03:25 -070037import android.content.Context;
38import android.content.Intent;
Christopher Tate3799bc22009-05-06 16:13:56 -070039import android.content.IntentFilter;
Dan Egnor87a02bc2009-06-17 02:30:10 -070040import android.content.ServiceConnection;
Christopher Tate181fafa2009-05-14 11:12:14 -070041import android.content.pm.ApplicationInfo;
Christopher Tatec7b31e32009-06-10 15:49:30 -070042import android.content.pm.IPackageDataObserver;
Christopher Tatea858cb02011-06-03 12:27:51 -070043import android.content.pm.IPackageDeleteObserver;
Christopher Tate75a99702011-05-18 16:28:19 -070044import android.content.pm.IPackageInstallObserver;
Christopher Tate1bb69062010-02-19 17:02:12 -080045import android.content.pm.IPackageManager;
Christopher Tate7b881282009-06-07 13:52:37 -070046import android.content.pm.PackageInfo;
Dan Egnor87a02bc2009-06-17 02:30:10 -070047import android.content.pm.PackageManager;
Jason parks1125d782011-01-12 09:47:26 -060048import android.content.pm.Signature;
Jason parksa3cdaa52011-01-13 14:15:43 -060049import android.content.pm.PackageManager.NameNotFoundException;
Christopher Tate3799bc22009-05-06 16:13:56 -070050import android.net.Uri;
Christopher Tate487529a2009-04-29 14:03:25 -070051import android.os.Binder;
Christopher Tate75a99702011-05-18 16:28:19 -070052import android.os.Build;
Christopher Tate3799bc22009-05-06 16:13:56 -070053import android.os.Bundle;
Christopher Tate22b87872009-05-04 16:41:53 -070054import android.os.Environment;
Christopher Tate487529a2009-04-29 14:03:25 -070055import android.os.Handler;
Christopher Tate44a27902010-01-27 17:15:49 -080056import android.os.HandlerThread;
Christopher Tate487529a2009-04-29 14:03:25 -070057import android.os.IBinder;
Christopher Tate44a27902010-01-27 17:15:49 -080058import android.os.Looper;
Christopher Tate487529a2009-04-29 14:03:25 -070059import android.os.Message;
Christopher Tate22b87872009-05-04 16:41:53 -070060import android.os.ParcelFileDescriptor;
Christopher Tateb6787f22009-07-02 17:40:45 -070061import android.os.PowerManager;
Christopher Tate043dadc2009-06-02 16:11:00 -070062import android.os.Process;
Christopher Tate487529a2009-04-29 14:03:25 -070063import android.os.RemoteException;
Dan Egnorbb9001c2009-07-27 12:20:13 -070064import android.os.SystemClock;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070065import android.os.WorkSource;
Oscar Montemayora8529f62009-11-18 10:14:20 -080066import android.provider.Settings;
Dan Egnorbb9001c2009-07-27 12:20:13 -070067import android.util.EventLog;
Christopher Tate79ec80d2011-06-24 14:58:49 -070068import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080069import android.util.Slog;
Christopher Tate487529a2009-04-29 14:03:25 -070070import android.util.SparseArray;
Christopher Tate44a27902010-01-27 17:15:49 -080071import android.util.SparseIntArray;
Christopher Tate4a627c72011-04-01 14:43:32 -070072import android.util.StringBuilderPrinter;
73
Jason parksa3cdaa52011-01-13 14:15:43 -060074import com.android.internal.backup.BackupConstants;
75import com.android.internal.backup.IBackupTransport;
76import com.android.internal.backup.LocalTransport;
77import com.android.server.PackageManagerBackupAgent.Metadata;
78
Christopher Tate7926a692011-07-11 11:31:57 -070079import java.io.DataInputStream;
Christopher Tatecde87f42009-06-12 12:55:53 -070080import java.io.EOFException;
Christopher Tate22b87872009-05-04 16:41:53 -070081import java.io.File;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070082import java.io.FileDescriptor;
Christopher Tate75a99702011-05-18 16:28:19 -070083import java.io.FileInputStream;
Christopher Tate1168baa2010-02-17 13:03:40 -080084import java.io.FileNotFoundException;
Christopher Tate4cc86e12009-09-21 19:36:51 -070085import java.io.FileOutputStream;
Christopher Tatec7b31e32009-06-10 15:49:30 -070086import java.io.IOException;
Christopher Tate75a99702011-05-18 16:28:19 -070087import java.io.InputStream;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070088import java.io.PrintWriter;
Christopher Tatecde87f42009-06-12 12:55:53 -070089import java.io.RandomAccessFile;
Christopher Tate75a99702011-05-18 16:28:19 -070090import java.text.SimpleDateFormat;
Joe Onorato8ad02812009-05-13 01:41:44 -040091import java.util.ArrayList;
Christopher Tate75a99702011-05-18 16:28:19 -070092import java.util.Date;
Joe Onorato8ad02812009-05-13 01:41:44 -040093import java.util.HashMap;
Christopher Tate487529a2009-04-29 14:03:25 -070094import java.util.HashSet;
95import java.util.List;
Christopher Tate91717492009-06-26 21:07:13 -070096import java.util.Map;
Dan Egnorc1c49c02009-10-30 17:35:39 -070097import java.util.Random;
Christopher Tateb49ceb32010-02-08 16:22:24 -080098import java.util.Set;
Christopher Tate4a627c72011-04-01 14:43:32 -070099import java.util.concurrent.atomic.AtomicBoolean;
Christopher Tate7926a692011-07-11 11:31:57 -0700100import java.util.zip.Deflater;
101import java.util.zip.DeflaterOutputStream;
102import java.util.zip.Inflater;
103import java.util.zip.InflaterInputStream;
Christopher Tate487529a2009-04-29 14:03:25 -0700104
105class BackupManagerService extends IBackupManager.Stub {
106 private static final String TAG = "BackupManagerService";
Christopher Tate4a627c72011-04-01 14:43:32 -0700107 private static final boolean DEBUG = true;
108
109 // Name and current contents version of the full-backup manifest file
110 static final String BACKUP_MANIFEST_FILENAME = "_manifest";
111 static final int BACKUP_MANIFEST_VERSION = 1;
Christopher Tateaa088442009-06-16 18:25:46 -0700112
Christopher Tate49401dd2009-07-01 12:34:29 -0700113 // How often we perform a backup pass. Privileged external callers can
114 // trigger an immediate pass.
Christopher Tateb6787f22009-07-02 17:40:45 -0700115 private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
Christopher Tate487529a2009-04-29 14:03:25 -0700116
Dan Egnorc1c49c02009-10-30 17:35:39 -0700117 // Random variation in backup scheduling time to avoid server load spikes
118 private static final int FUZZ_MILLIS = 5 * 60 * 1000;
119
Christopher Tate8031a3d2009-07-06 16:36:05 -0700120 // The amount of time between the initial provisioning of the device and
121 // the first backup pass.
122 private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
123
Christopher Tate45281862010-03-05 15:46:30 -0800124 private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
125 private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
126 private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
Christopher Tate487529a2009-04-29 14:03:25 -0700127 private static final int MSG_RUN_BACKUP = 1;
Christopher Tate043dadc2009-06-02 16:11:00 -0700128 private static final int MSG_RUN_FULL_BACKUP = 2;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700129 private static final int MSG_RUN_RESTORE = 3;
Christopher Tateee0e78a2009-07-02 11:17:03 -0700130 private static final int MSG_RUN_CLEAR = 4;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700131 private static final int MSG_RUN_INITIALIZE = 5;
Christopher Tate2d449afe2010-03-29 19:14:24 -0700132 private static final int MSG_RUN_GET_RESTORE_SETS = 6;
133 private static final int MSG_TIMEOUT = 7;
Christopher Tate73a3cb32010-12-13 18:27:26 -0800134 private static final int MSG_RESTORE_TIMEOUT = 8;
Christopher Tate4a627c72011-04-01 14:43:32 -0700135 private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
136 private static final int MSG_RUN_FULL_RESTORE = 10;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700137
138 // Timeout interval for deciding that a bind or clear-data has taken too long
139 static final long TIMEOUT_INTERVAL = 10 * 1000;
140
Christopher Tate44a27902010-01-27 17:15:49 -0800141 // Timeout intervals for agent backup & restore operations
142 static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
Christopher Tate4a627c72011-04-01 14:43:32 -0700143 static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
Christopher Tateb0628bf2011-06-02 15:08:13 -0700144 static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
Christopher Tate44a27902010-01-27 17:15:49 -0800145 static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
146
Christopher Tate4a627c72011-04-01 14:43:32 -0700147 // User confirmation timeout for a full backup/restore operation
148 static final long TIMEOUT_FULL_CONFIRMATION = 30 * 1000;
149
Christopher Tate487529a2009-04-29 14:03:25 -0700150 private Context mContext;
151 private PackageManager mPackageManager;
Christopher Tate1bb69062010-02-19 17:02:12 -0800152 IPackageManager mPackageManagerBinder;
Christopher Tate6ef58a12009-06-29 14:56:28 -0700153 private IActivityManager mActivityManager;
Christopher Tateb6787f22009-07-02 17:40:45 -0700154 private PowerManager mPowerManager;
155 private AlarmManager mAlarmManager;
Christopher Tate44a27902010-01-27 17:15:49 -0800156 IBackupManager mBackupManagerBinder;
Christopher Tateb6787f22009-07-02 17:40:45 -0700157
Christopher Tate73e02522009-07-15 14:18:26 -0700158 boolean mEnabled; // access to this is synchronized on 'this'
159 boolean mProvisioned;
Christopher Tatecce9da52010-02-03 15:11:15 -0800160 boolean mAutoRestore;
Christopher Tate73e02522009-07-15 14:18:26 -0700161 PowerManager.WakeLock mWakelock;
Christopher Tate44a27902010-01-27 17:15:49 -0800162 HandlerThread mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
163 BackupHandler mBackupHandler;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700164 PendingIntent mRunBackupIntent, mRunInitIntent;
165 BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
Christopher Tate487529a2009-04-29 14:03:25 -0700166 // map UIDs to the set of backup client services within that UID's app set
Christopher Tate73e02522009-07-15 14:18:26 -0700167 final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
Christopher Tate181fafa2009-05-14 11:12:14 -0700168 = new SparseArray<HashSet<ApplicationInfo>>();
Christopher Tate487529a2009-04-29 14:03:25 -0700169 // set of backup services that have pending changes
Christopher Tate73e02522009-07-15 14:18:26 -0700170 class BackupRequest {
Christopher Tate181fafa2009-05-14 11:12:14 -0700171 public ApplicationInfo appInfo;
Christopher Tateaa088442009-06-16 18:25:46 -0700172
Christopher Tate4a627c72011-04-01 14:43:32 -0700173 BackupRequest(ApplicationInfo app) {
Christopher Tate181fafa2009-05-14 11:12:14 -0700174 appInfo = app;
Christopher Tate46758122009-05-06 11:22:00 -0700175 }
Christopher Tate181fafa2009-05-14 11:12:14 -0700176
177 public String toString() {
Christopher Tate4a627c72011-04-01 14:43:32 -0700178 return "BackupRequest{app=" + appInfo + "}";
Christopher Tate181fafa2009-05-14 11:12:14 -0700179 }
Christopher Tate46758122009-05-06 11:22:00 -0700180 }
Christopher Tatec28083a2010-12-14 16:16:44 -0800181 // Backups that we haven't started yet. Keys are package names.
182 HashMap<String,BackupRequest> mPendingBackups
183 = new HashMap<String,BackupRequest>();
Christopher Tate5cb400b2009-06-25 16:03:14 -0700184
185 // Pseudoname that we use for the Package Manager metadata "package"
Christopher Tate73e02522009-07-15 14:18:26 -0700186 static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
Christopher Tate6aa41f42009-06-19 14:14:22 -0700187
188 // locking around the pending-backup management
Christopher Tate73e02522009-07-15 14:18:26 -0700189 final Object mQueueLock = new Object();
Christopher Tate487529a2009-04-29 14:03:25 -0700190
Christopher Tate043dadc2009-06-02 16:11:00 -0700191 // The thread performing the sequence of queued backups binds to each app's agent
192 // in succession. Bind notifications are asynchronously delivered through the
193 // Activity Manager; use this lock object to signal when a requested binding has
194 // completed.
Christopher Tate73e02522009-07-15 14:18:26 -0700195 final Object mAgentConnectLock = new Object();
196 IBackupAgent mConnectedAgent;
197 volatile boolean mConnecting;
Christopher Tate55f931a2009-09-29 17:17:34 -0700198 volatile long mLastBackupPass;
199 volatile long mNextBackupPass;
Christopher Tate043dadc2009-06-02 16:11:00 -0700200
Christopher Tate55f931a2009-09-29 17:17:34 -0700201 // A similar synchronization mechanism around clearing apps' data for restore
Christopher Tate73e02522009-07-15 14:18:26 -0700202 final Object mClearDataLock = new Object();
203 volatile boolean mClearingData;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700204
Christopher Tate91717492009-06-26 21:07:13 -0700205 // Transport bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700206 final HashMap<String,IBackupTransport> mTransports
Christopher Tate91717492009-06-26 21:07:13 -0700207 = new HashMap<String,IBackupTransport>();
Christopher Tate73e02522009-07-15 14:18:26 -0700208 String mCurrentTransport;
209 IBackupTransport mLocalTransport, mGoogleTransport;
Christopher Tate80202c82010-01-25 19:37:47 -0800210 ActiveRestoreSession mActiveRestoreSession;
Christopher Tate043dadc2009-06-02 16:11:00 -0700211
Christopher Tate2d449afe2010-03-29 19:14:24 -0700212 class RestoreGetSetsParams {
213 public IBackupTransport transport;
214 public ActiveRestoreSession session;
215 public IRestoreObserver observer;
216
217 RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
218 IRestoreObserver _observer) {
219 transport = _transport;
220 session = _session;
221 observer = _observer;
222 }
223 }
224
Christopher Tate73e02522009-07-15 14:18:26 -0700225 class RestoreParams {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700226 public IBackupTransport transport;
227 public IRestoreObserver observer;
Dan Egnor156411d2009-06-26 13:20:02 -0700228 public long token;
Christopher Tate84725812010-02-04 15:52:40 -0800229 public PackageInfo pkgInfo;
Christopher Tate1bb69062010-02-19 17:02:12 -0800230 public int pmToken; // in post-install restore, the PM's token for this transaction
Chris Tate249345b2010-10-29 12:57:04 -0700231 public boolean needFullBackup;
Christopher Tate284f1bb2011-07-07 14:31:18 -0700232 public String[] filterSet;
Christopher Tate84725812010-02-04 15:52:40 -0800233
234 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
Chris Tate249345b2010-10-29 12:57:04 -0700235 long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
Christopher Tate84725812010-02-04 15:52:40 -0800236 transport = _transport;
237 observer = _obs;
238 token = _token;
239 pkgInfo = _pkg;
Christopher Tate1bb69062010-02-19 17:02:12 -0800240 pmToken = _pmToken;
Chris Tate249345b2010-10-29 12:57:04 -0700241 needFullBackup = _needFullBackup;
Christopher Tate284f1bb2011-07-07 14:31:18 -0700242 filterSet = null;
Christopher Tate84725812010-02-04 15:52:40 -0800243 }
Christopher Tate7d562ec2009-06-25 18:03:43 -0700244
Chris Tate249345b2010-10-29 12:57:04 -0700245 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
246 boolean _needFullBackup) {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700247 transport = _transport;
248 observer = _obs;
Dan Egnor156411d2009-06-26 13:20:02 -0700249 token = _token;
Christopher Tate84725812010-02-04 15:52:40 -0800250 pkgInfo = null;
Christopher Tate1bb69062010-02-19 17:02:12 -0800251 pmToken = 0;
Chris Tate249345b2010-10-29 12:57:04 -0700252 needFullBackup = _needFullBackup;
Christopher Tate284f1bb2011-07-07 14:31:18 -0700253 filterSet = null;
254 }
255
256 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
257 String[] _filterSet, boolean _needFullBackup) {
258 transport = _transport;
259 observer = _obs;
260 token = _token;
261 pkgInfo = null;
262 pmToken = 0;
263 needFullBackup = _needFullBackup;
264 filterSet = _filterSet;
Christopher Tate7d562ec2009-06-25 18:03:43 -0700265 }
266 }
267
Christopher Tate73e02522009-07-15 14:18:26 -0700268 class ClearParams {
Christopher Tateee0e78a2009-07-02 11:17:03 -0700269 public IBackupTransport transport;
270 public PackageInfo packageInfo;
271
272 ClearParams(IBackupTransport _transport, PackageInfo _info) {
273 transport = _transport;
274 packageInfo = _info;
275 }
276 }
277
Christopher Tate4a627c72011-04-01 14:43:32 -0700278 class FullParams {
279 public ParcelFileDescriptor fd;
280 public final AtomicBoolean latch;
281 public IFullBackupRestoreObserver observer;
282
283 FullParams() {
284 latch = new AtomicBoolean(false);
285 }
286 }
287
288 class FullBackupParams extends FullParams {
289 public boolean includeApks;
290 public boolean includeShared;
291 public boolean allApps;
292 public String[] packages;
293
294 FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
295 boolean doAllApps, String[] pkgList) {
296 fd = output;
297 includeApks = saveApks;
298 includeShared = saveShared;
299 allApps = doAllApps;
300 packages = pkgList;
301 }
302 }
303
304 class FullRestoreParams extends FullParams {
305 FullRestoreParams(ParcelFileDescriptor input) {
306 fd = input;
307 }
308 }
309
Christopher Tate44a27902010-01-27 17:15:49 -0800310 // Bookkeeping of in-flight operations for timeout etc. purposes. The operation
311 // token is the index of the entry in the pending-operations list.
312 static final int OP_PENDING = 0;
313 static final int OP_ACKNOWLEDGED = 1;
314 static final int OP_TIMEOUT = -1;
315
316 final SparseIntArray mCurrentOperations = new SparseIntArray();
317 final Object mCurrentOpLock = new Object();
318 final Random mTokenGenerator = new Random();
319
Christopher Tate4a627c72011-04-01 14:43:32 -0700320 final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
321
Christopher Tate5cb400b2009-06-25 16:03:14 -0700322 // Where we keep our journal files and other bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700323 File mBaseStateDir;
324 File mDataDir;
325 File mJournalDir;
326 File mJournal;
Christopher Tate73e02522009-07-15 14:18:26 -0700327
Christopher Tate84725812010-02-04 15:52:40 -0800328 // Keep a log of all the apps we've ever backed up, and what the
329 // dataset tokens are for both the current backup dataset and
330 // the ancestral dataset.
Christopher Tate73e02522009-07-15 14:18:26 -0700331 private File mEverStored;
Christopher Tate73e02522009-07-15 14:18:26 -0700332 HashSet<String> mEverStoredApps = new HashSet<String>();
333
Christopher Tateb49ceb32010-02-08 16:22:24 -0800334 static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes
Christopher Tate84725812010-02-04 15:52:40 -0800335 File mTokenFile;
Christopher Tateb49ceb32010-02-08 16:22:24 -0800336 Set<String> mAncestralPackages = null;
Christopher Tate84725812010-02-04 15:52:40 -0800337 long mAncestralToken = 0;
338 long mCurrentToken = 0;
339
Christopher Tate4cc86e12009-09-21 19:36:51 -0700340 // Persistently track the need to do a full init
341 static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
342 HashSet<String> mPendingInits = new HashSet<String>(); // transport names
Christopher Tateaa088442009-06-16 18:25:46 -0700343
Christopher Tate4a627c72011-04-01 14:43:32 -0700344 // Utility: build a new random integer token
345 int generateToken() {
346 int token;
347 do {
348 synchronized (mTokenGenerator) {
349 token = mTokenGenerator.nextInt();
350 }
351 } while (token < 0);
352 return token;
353 }
354
Christopher Tate44a27902010-01-27 17:15:49 -0800355 // ----- Asynchronous backup/restore handler thread -----
356
357 private class BackupHandler extends Handler {
358 public BackupHandler(Looper looper) {
359 super(looper);
360 }
361
362 public void handleMessage(Message msg) {
363
364 switch (msg.what) {
365 case MSG_RUN_BACKUP:
366 {
367 mLastBackupPass = System.currentTimeMillis();
368 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
369
370 IBackupTransport transport = getTransport(mCurrentTransport);
371 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800372 Slog.v(TAG, "Backup requested but no transport available");
Christopher Tate44a27902010-01-27 17:15:49 -0800373 mWakelock.release();
374 break;
375 }
376
377 // snapshot the pending-backup set and work on that
378 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
Christopher Tatec61da312010-02-05 10:41:27 -0800379 File oldJournal = mJournal;
Christopher Tate44a27902010-01-27 17:15:49 -0800380 synchronized (mQueueLock) {
Christopher Tatec61da312010-02-05 10:41:27 -0800381 // Do we have any work to do? Construct the work queue
382 // then release the synchronization lock to actually run
383 // the backup.
Christopher Tate44a27902010-01-27 17:15:49 -0800384 if (mPendingBackups.size() > 0) {
385 for (BackupRequest b: mPendingBackups.values()) {
386 queue.add(b);
387 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800388 if (DEBUG) Slog.v(TAG, "clearing pending backups");
Christopher Tate44a27902010-01-27 17:15:49 -0800389 mPendingBackups.clear();
390
391 // Start a new backup-queue journal file too
Christopher Tate44a27902010-01-27 17:15:49 -0800392 mJournal = null;
393
Christopher Tate44a27902010-01-27 17:15:49 -0800394 }
395 }
Christopher Tatec61da312010-02-05 10:41:27 -0800396
397 if (queue.size() > 0) {
398 // At this point, we have started a new journal file, and the old
399 // file identity is being passed to the backup processing thread.
400 // When it completes successfully, that old journal file will be
401 // deleted. If we crash prior to that, the old journal is parsed
402 // at next boot and the journaled requests fulfilled.
403 (new PerformBackupTask(transport, queue, oldJournal)).run();
404 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800405 Slog.v(TAG, "Backup requested but nothing pending");
Christopher Tatec61da312010-02-05 10:41:27 -0800406 mWakelock.release();
407 }
Christopher Tate44a27902010-01-27 17:15:49 -0800408 break;
409 }
410
411 case MSG_RUN_FULL_BACKUP:
Christopher Tate4a627c72011-04-01 14:43:32 -0700412 {
413 FullBackupParams params = (FullBackupParams)msg.obj;
414 (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
415 params.includeShared, params.allApps, params.packages,
416 params.latch)).run();
Christopher Tate44a27902010-01-27 17:15:49 -0800417 break;
Christopher Tate4a627c72011-04-01 14:43:32 -0700418 }
Christopher Tate44a27902010-01-27 17:15:49 -0800419
420 case MSG_RUN_RESTORE:
421 {
422 RestoreParams params = (RestoreParams)msg.obj;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800423 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
Christopher Tate44a27902010-01-27 17:15:49 -0800424 (new PerformRestoreTask(params.transport, params.observer,
Chris Tate249345b2010-10-29 12:57:04 -0700425 params.token, params.pkgInfo, params.pmToken,
Christopher Tate284f1bb2011-07-07 14:31:18 -0700426 params.needFullBackup, params.filterSet)).run();
Christopher Tate44a27902010-01-27 17:15:49 -0800427 break;
428 }
429
Christopher Tate75a99702011-05-18 16:28:19 -0700430 case MSG_RUN_FULL_RESTORE:
431 {
432 FullRestoreParams params = (FullRestoreParams)msg.obj;
433 (new PerformFullRestoreTask(params.fd, params.observer, params.latch)).run();
434 break;
435 }
436
Christopher Tate44a27902010-01-27 17:15:49 -0800437 case MSG_RUN_CLEAR:
438 {
439 ClearParams params = (ClearParams)msg.obj;
440 (new PerformClearTask(params.transport, params.packageInfo)).run();
441 break;
442 }
443
444 case MSG_RUN_INITIALIZE:
445 {
446 HashSet<String> queue;
447
448 // Snapshot the pending-init queue and work on that
449 synchronized (mQueueLock) {
450 queue = new HashSet<String>(mPendingInits);
451 mPendingInits.clear();
452 }
453
454 (new PerformInitializeTask(queue)).run();
455 break;
456 }
457
Christopher Tate2d449afe2010-03-29 19:14:24 -0700458 case MSG_RUN_GET_RESTORE_SETS:
459 {
460 // Like other async operations, this is entered with the wakelock held
461 RestoreSet[] sets = null;
462 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
463 try {
464 sets = params.transport.getAvailableRestoreSets();
465 // cache the result in the active session
466 synchronized (params.session) {
467 params.session.mRestoreSets = sets;
468 }
469 if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
470 } catch (Exception e) {
471 Slog.e(TAG, "Error from transport getting set list");
472 } finally {
473 if (params.observer != null) {
474 try {
475 params.observer.restoreSetsAvailable(sets);
476 } catch (RemoteException re) {
477 Slog.e(TAG, "Unable to report listing to observer");
478 } catch (Exception e) {
479 Slog.e(TAG, "Restore observer threw", e);
480 }
481 }
482
Christopher Tate2a935092011-03-03 17:30:32 -0800483 // Done: reset the session timeout clock
484 removeMessages(MSG_RESTORE_TIMEOUT);
485 sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
486
Christopher Tate2d449afe2010-03-29 19:14:24 -0700487 mWakelock.release();
488 }
489 break;
490 }
491
Christopher Tate44a27902010-01-27 17:15:49 -0800492 case MSG_TIMEOUT:
493 {
494 synchronized (mCurrentOpLock) {
495 final int token = msg.arg1;
496 int state = mCurrentOperations.get(token, OP_TIMEOUT);
497 if (state == OP_PENDING) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800498 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + token);
Christopher Tate44a27902010-01-27 17:15:49 -0800499 mCurrentOperations.put(token, OP_TIMEOUT);
500 }
501 mCurrentOpLock.notifyAll();
502 }
503 break;
504 }
Christopher Tate73a3cb32010-12-13 18:27:26 -0800505
506 case MSG_RESTORE_TIMEOUT:
507 {
508 synchronized (BackupManagerService.this) {
509 if (mActiveRestoreSession != null) {
510 // Client app left the restore session dangling. We know that it
511 // can't be in the middle of an actual restore operation because
512 // those are executed serially on this same handler thread. Clean
513 // up now.
514 Slog.w(TAG, "Restore session timed out; aborting");
515 post(mActiveRestoreSession.new EndRestoreRunnable(
516 BackupManagerService.this, mActiveRestoreSession));
517 }
518 }
519 }
Christopher Tate4a627c72011-04-01 14:43:32 -0700520
521 case MSG_FULL_CONFIRMATION_TIMEOUT:
522 {
523 synchronized (mFullConfirmations) {
524 FullParams params = mFullConfirmations.get(msg.arg1);
525 if (params != null) {
526 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
527
528 // Release the waiter; timeout == completion
529 signalFullBackupRestoreCompletion(params);
530
531 // Remove the token from the set
532 mFullConfirmations.delete(msg.arg1);
533
534 // Report a timeout to the observer, if any
535 if (params.observer != null) {
536 try {
537 params.observer.onTimeout();
538 } catch (RemoteException e) {
539 /* don't care if the app has gone away */
540 }
541 }
542 } else {
543 Slog.d(TAG, "couldn't find params for token " + msg.arg1);
544 }
545 }
546 break;
547 }
Christopher Tate44a27902010-01-27 17:15:49 -0800548 }
549 }
550 }
551
552 // ----- Main service implementation -----
553
Christopher Tate487529a2009-04-29 14:03:25 -0700554 public BackupManagerService(Context context) {
555 mContext = context;
556 mPackageManager = context.getPackageManager();
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -0700557 mPackageManagerBinder = AppGlobals.getPackageManager();
Christopher Tate181fafa2009-05-14 11:12:14 -0700558 mActivityManager = ActivityManagerNative.getDefault();
Christopher Tate487529a2009-04-29 14:03:25 -0700559
Christopher Tateb6787f22009-07-02 17:40:45 -0700560 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
561 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
562
Christopher Tate44a27902010-01-27 17:15:49 -0800563 mBackupManagerBinder = asInterface(asBinder());
564
565 // spin up the backup/restore handler thread
566 mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
567 mHandlerThread.start();
568 mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
569
Christopher Tate22b87872009-05-04 16:41:53 -0700570 // Set up our bookkeeping
Christopher Tateb6787f22009-07-02 17:40:45 -0700571 boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
Dianne Hackborncf098292009-07-01 19:55:20 -0700572 Settings.Secure.BACKUP_ENABLED, 0) != 0;
Christopher Tate8031a3d2009-07-06 16:36:05 -0700573 mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
Joe Onoratoab9a2a52009-07-27 08:56:39 -0700574 Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
Christopher Tatecce9da52010-02-03 15:11:15 -0800575 mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
Christopher Tate5035fda2010-02-25 18:01:14 -0800576 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
Oscar Montemayora8529f62009-11-18 10:14:20 -0800577 // If Encrypted file systems is enabled or disabled, this call will return the
578 // correct directory.
Jason parksa3cdaa52011-01-13 14:15:43 -0600579 mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
Oscar Montemayora8529f62009-11-18 10:14:20 -0800580 mBaseStateDir.mkdirs();
Christopher Tatef4172472009-05-05 15:50:03 -0700581 mDataDir = Environment.getDownloadCacheDirectory();
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700582
Christopher Tate4cc86e12009-09-21 19:36:51 -0700583 // Alarm receivers for scheduled backups & initialization operations
Christopher Tateb6787f22009-07-02 17:40:45 -0700584 mRunBackupReceiver = new RunBackupReceiver();
Christopher Tate4cc86e12009-09-21 19:36:51 -0700585 IntentFilter filter = new IntentFilter();
586 filter.addAction(RUN_BACKUP_ACTION);
587 context.registerReceiver(mRunBackupReceiver, filter,
588 android.Manifest.permission.BACKUP, null);
589
590 mRunInitReceiver = new RunInitializeReceiver();
591 filter = new IntentFilter();
592 filter.addAction(RUN_INITIALIZE_ACTION);
593 context.registerReceiver(mRunInitReceiver, filter,
594 android.Manifest.permission.BACKUP, null);
Christopher Tateb6787f22009-07-02 17:40:45 -0700595
596 Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
Christopher Tateb6787f22009-07-02 17:40:45 -0700597 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
598 mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
599
Christopher Tate4cc86e12009-09-21 19:36:51 -0700600 Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
601 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
602 mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
603
Christopher Tatecde87f42009-06-12 12:55:53 -0700604 // Set up the backup-request journaling
Christopher Tate5cb400b2009-06-25 16:03:14 -0700605 mJournalDir = new File(mBaseStateDir, "pending");
606 mJournalDir.mkdirs(); // creates mBaseStateDir along the way
Dan Egnor852f8e42009-09-30 11:20:45 -0700607 mJournal = null; // will be created on first use
Christopher Tatecde87f42009-06-12 12:55:53 -0700608
Christopher Tate73e02522009-07-15 14:18:26 -0700609 // Set up the various sorts of package tracking we do
610 initPackageTracking();
611
Christopher Tateabce4e82009-06-18 18:35:32 -0700612 // Build our mapping of uid to backup client services. This implicitly
613 // schedules a backup pass on the Package Manager metadata the first
614 // time anything needs to be backed up.
Christopher Tate3799bc22009-05-06 16:13:56 -0700615 synchronized (mBackupParticipants) {
616 addPackageParticipantsLocked(null);
Christopher Tate487529a2009-04-29 14:03:25 -0700617 }
618
Dan Egnor87a02bc2009-06-17 02:30:10 -0700619 // Set up our transport options and initialize the default transport
620 // TODO: Have transports register themselves somehow?
621 // TODO: Don't create transports that we don't need to?
Dan Egnor87a02bc2009-06-17 02:30:10 -0700622 mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
Christopher Tate91717492009-06-26 21:07:13 -0700623 ComponentName localName = new ComponentName(context, LocalTransport.class);
624 registerTransport(localName.flattenToShortString(), mLocalTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700625
Christopher Tate91717492009-06-26 21:07:13 -0700626 mGoogleTransport = null;
Dianne Hackborncf098292009-07-01 19:55:20 -0700627 mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
628 Settings.Secure.BACKUP_TRANSPORT);
629 if ("".equals(mCurrentTransport)) {
630 mCurrentTransport = null;
Christopher Tatece0bf062009-07-01 11:43:53 -0700631 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800632 if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -0700633
634 // Attach to the Google backup transport. When this comes up, it will set
635 // itself as the current transport because we explicitly reset mCurrentTransport
636 // to null.
Christopher Tatea32504f2010-04-21 17:58:07 -0700637 ComponentName transportComponent = new ComponentName("com.google.android.backup",
638 "com.google.android.backup.BackupTransportService");
639 try {
640 // If there's something out there that is supposed to be the Google
641 // backup transport, make sure it's legitimately part of the OS build
642 // and not an app lying about its package name.
643 ApplicationInfo info = mPackageManager.getApplicationInfo(
644 transportComponent.getPackageName(), 0);
645 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
646 if (DEBUG) Slog.v(TAG, "Binding to Google transport");
647 Intent intent = new Intent().setComponent(transportComponent);
648 context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
649 } else {
650 Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
651 }
652 } catch (PackageManager.NameNotFoundException nnf) {
653 // No such package? No binding.
654 if (DEBUG) Slog.v(TAG, "Google transport not present");
655 }
Christopher Tateaa088442009-06-16 18:25:46 -0700656
Christopher Tatecde87f42009-06-12 12:55:53 -0700657 // Now that we know about valid backup participants, parse any
Christopher Tate49401dd2009-07-01 12:34:29 -0700658 // leftover journal files into the pending backup set
Christopher Tatecde87f42009-06-12 12:55:53 -0700659 parseLeftoverJournals();
660
Christopher Tateb6787f22009-07-02 17:40:45 -0700661 // Power management
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700662 mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
Christopher Tateb6787f22009-07-02 17:40:45 -0700663
664 // Start the backup passes going
665 setBackupEnabled(areEnabled);
666 }
667
668 private class RunBackupReceiver extends BroadcastReceiver {
669 public void onReceive(Context context, Intent intent) {
670 if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
Christopher Tateb6787f22009-07-02 17:40:45 -0700671 synchronized (mQueueLock) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700672 if (mPendingInits.size() > 0) {
673 // If there are pending init operations, we process those
674 // and then settle into the usual periodic backup schedule.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800675 if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700676 try {
677 mAlarmManager.cancel(mRunInitIntent);
678 mRunInitIntent.send();
679 } catch (PendingIntent.CanceledException ce) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800680 Slog.e(TAG, "Run init intent cancelled");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700681 // can't really do more than bail here
682 }
683 } else {
Christopher Tatec2af5d32010-02-02 15:18:58 -0800684 // Don't run backups now if we're disabled or not yet
685 // fully set up.
686 if (mEnabled && mProvisioned) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800687 if (DEBUG) Slog.v(TAG, "Running a backup pass");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700688
689 // Acquire the wakelock and pass it to the backup thread. it will
690 // be released once backup concludes.
691 mWakelock.acquire();
692
693 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
694 mBackupHandler.sendMessage(msg);
695 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800696 Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
Christopher Tate4cc86e12009-09-21 19:36:51 -0700697 }
698 }
699 }
700 }
701 }
702 }
703
704 private class RunInitializeReceiver extends BroadcastReceiver {
705 public void onReceive(Context context, Intent intent) {
706 if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
707 synchronized (mQueueLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800708 if (DEBUG) Slog.v(TAG, "Running a device init");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700709
710 // Acquire the wakelock and pass it to the init thread. it will
711 // be released once init concludes.
Christopher Tateb6787f22009-07-02 17:40:45 -0700712 mWakelock.acquire();
713
Christopher Tate4cc86e12009-09-21 19:36:51 -0700714 Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
Christopher Tateb6787f22009-07-02 17:40:45 -0700715 mBackupHandler.sendMessage(msg);
716 }
717 }
Christopher Tate49401dd2009-07-01 12:34:29 -0700718 }
Christopher Tateb6787f22009-07-02 17:40:45 -0700719 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700720
Christopher Tate73e02522009-07-15 14:18:26 -0700721 private void initPackageTracking() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800722 if (DEBUG) Slog.v(TAG, "Initializing package tracking");
Christopher Tate73e02522009-07-15 14:18:26 -0700723
Christopher Tate84725812010-02-04 15:52:40 -0800724 // Remember our ancestral dataset
725 mTokenFile = new File(mBaseStateDir, "ancestral");
726 try {
727 RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
Christopher Tateb49ceb32010-02-08 16:22:24 -0800728 int version = tf.readInt();
729 if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
730 mAncestralToken = tf.readLong();
731 mCurrentToken = tf.readLong();
732
733 int numPackages = tf.readInt();
734 if (numPackages >= 0) {
735 mAncestralPackages = new HashSet<String>();
736 for (int i = 0; i < numPackages; i++) {
737 String pkgName = tf.readUTF();
738 mAncestralPackages.add(pkgName);
739 }
740 }
741 }
Brad Fitzpatrick725d8f02010-11-15 11:12:42 -0800742 tf.close();
Christopher Tate1168baa2010-02-17 13:03:40 -0800743 } catch (FileNotFoundException fnf) {
744 // Probably innocuous
Joe Onorato8a9b2202010-02-26 18:56:32 -0800745 Slog.v(TAG, "No ancestral data");
Christopher Tate84725812010-02-04 15:52:40 -0800746 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800747 Slog.w(TAG, "Unable to read token file", e);
Christopher Tate84725812010-02-04 15:52:40 -0800748 }
749
Christopher Tatee97e8072009-07-15 16:45:50 -0700750 // Keep a log of what apps we've ever backed up. Because we might have
751 // rebooted in the middle of an operation that was removing something from
752 // this log, we sanity-check its contents here and reconstruct it.
Christopher Tate73e02522009-07-15 14:18:26 -0700753 mEverStored = new File(mBaseStateDir, "processed");
Christopher Tatee97e8072009-07-15 16:45:50 -0700754 File tempProcessedFile = new File(mBaseStateDir, "processed.new");
Christopher Tate73e02522009-07-15 14:18:26 -0700755
Christopher Tatee97e8072009-07-15 16:45:50 -0700756 // If we were in the middle of removing something from the ever-backed-up
757 // file, there might be a transient "processed.new" file still present.
Dan Egnor852f8e42009-09-30 11:20:45 -0700758 // Ignore it -- we'll validate "processed" against the current package set.
Christopher Tatee97e8072009-07-15 16:45:50 -0700759 if (tempProcessedFile.exists()) {
760 tempProcessedFile.delete();
761 }
762
Dan Egnor852f8e42009-09-30 11:20:45 -0700763 // If there are previous contents, parse them out then start a new
764 // file to continue the recordkeeping.
765 if (mEverStored.exists()) {
766 RandomAccessFile temp = null;
767 RandomAccessFile in = null;
768
769 try {
770 temp = new RandomAccessFile(tempProcessedFile, "rws");
771 in = new RandomAccessFile(mEverStored, "r");
772
773 while (true) {
774 PackageInfo info;
775 String pkg = in.readUTF();
776 try {
777 info = mPackageManager.getPackageInfo(pkg, 0);
778 mEverStoredApps.add(pkg);
779 temp.writeUTF(pkg);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800780 if (DEBUG) Slog.v(TAG, " + " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700781 } catch (NameNotFoundException e) {
782 // nope, this package was uninstalled; don't include it
Joe Onorato8a9b2202010-02-26 18:56:32 -0800783 if (DEBUG) Slog.v(TAG, " - " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700784 }
785 }
786 } catch (EOFException e) {
787 // Once we've rewritten the backup history log, atomically replace the
788 // old one with the new one then reopen the file for continuing use.
789 if (!tempProcessedFile.renameTo(mEverStored)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800790 Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -0700791 }
792 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800793 Slog.e(TAG, "Error in processed file", e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700794 } finally {
795 try { if (temp != null) temp.close(); } catch (IOException e) {}
796 try { if (in != null) in.close(); } catch (IOException e) {}
797 }
798 }
799
Christopher Tate73e02522009-07-15 14:18:26 -0700800 // Register for broadcasts about package install, etc., so we can
801 // update the provider list.
802 IntentFilter filter = new IntentFilter();
803 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
804 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
805 filter.addDataScheme("package");
806 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800807 // Register for events related to sdcard installation.
808 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800809 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
810 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800811 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
Christopher Tate73e02522009-07-15 14:18:26 -0700812 }
813
Christopher Tatecde87f42009-06-12 12:55:53 -0700814 private void parseLeftoverJournals() {
Dan Egnor852f8e42009-09-30 11:20:45 -0700815 for (File f : mJournalDir.listFiles()) {
816 if (mJournal == null || f.compareTo(mJournal) != 0) {
817 // This isn't the current journal, so it must be a leftover. Read
818 // out the package names mentioned there and schedule them for
819 // backup.
820 RandomAccessFile in = null;
821 try {
Joe Onorato431bb222010-10-18 19:13:23 -0400822 Slog.i(TAG, "Found stale backup journal, scheduling");
Dan Egnor852f8e42009-09-30 11:20:45 -0700823 in = new RandomAccessFile(f, "r");
824 while (true) {
825 String packageName = in.readUTF();
Joe Onorato431bb222010-10-18 19:13:23 -0400826 Slog.i(TAG, " " + packageName);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -0700827 dataChangedImpl(packageName);
Christopher Tatecde87f42009-06-12 12:55:53 -0700828 }
Dan Egnor852f8e42009-09-30 11:20:45 -0700829 } catch (EOFException e) {
830 // no more data; we're done
831 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800832 Slog.e(TAG, "Can't read " + f, e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700833 } finally {
834 // close/delete the file
835 try { if (in != null) in.close(); } catch (IOException e) {}
836 f.delete();
Christopher Tatecde87f42009-06-12 12:55:53 -0700837 }
838 }
839 }
840 }
841
Christopher Tate4cc86e12009-09-21 19:36:51 -0700842 // Maintain persistent state around whether need to do an initialize operation.
843 // Must be called with the queue lock held.
844 void recordInitPendingLocked(boolean isPending, String transportName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800845 if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
Christopher Tate4cc86e12009-09-21 19:36:51 -0700846 + " on transport " + transportName);
847 try {
848 IBackupTransport transport = getTransport(transportName);
849 String transportDirName = transport.transportDirName();
850 File stateDir = new File(mBaseStateDir, transportDirName);
851 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
852
853 if (isPending) {
854 // We need an init before we can proceed with sending backup data.
855 // Record that with an entry in our set of pending inits, as well as
856 // journaling it via creation of a sentinel file.
857 mPendingInits.add(transportName);
858 try {
859 (new FileOutputStream(initPendingFile)).close();
860 } catch (IOException ioe) {
861 // Something is badly wrong with our permissions; just try to move on
862 }
863 } else {
864 // No more initialization needed; wipe the journal and reset our state.
865 initPendingFile.delete();
866 mPendingInits.remove(transportName);
867 }
868 } catch (RemoteException e) {
869 // can't happen; the transport is local
870 }
871 }
872
Christopher Tated55e18a2009-09-21 10:12:59 -0700873 // Reset all of our bookkeeping, in response to having been told that
874 // the backend data has been wiped [due to idle expiry, for example],
875 // so we must re-upload all saved settings.
876 void resetBackupState(File stateFileDir) {
877 synchronized (mQueueLock) {
878 // Wipe the "what we've ever backed up" tracking
Christopher Tated55e18a2009-09-21 10:12:59 -0700879 mEverStoredApps.clear();
Dan Egnor852f8e42009-09-30 11:20:45 -0700880 mEverStored.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -0700881
Christopher Tate84725812010-02-04 15:52:40 -0800882 mCurrentToken = 0;
883 writeRestoreTokens();
884
Christopher Tated55e18a2009-09-21 10:12:59 -0700885 // Remove all the state files
886 for (File sf : stateFileDir.listFiles()) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700887 // ... but don't touch the needs-init sentinel
888 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
889 sf.delete();
890 }
Christopher Tated55e18a2009-09-21 10:12:59 -0700891 }
Christopher Tate45597642011-04-04 16:59:21 -0700892 }
Christopher Tated55e18a2009-09-21 10:12:59 -0700893
Christopher Tate45597642011-04-04 16:59:21 -0700894 // Enqueue a new backup of every participant
895 int N = mBackupParticipants.size();
896 for (int i=0; i<N; i++) {
897 int uid = mBackupParticipants.keyAt(i);
898 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
899 for (ApplicationInfo app: participants) {
900 dataChangedImpl(app.packageName);
Christopher Tated55e18a2009-09-21 10:12:59 -0700901 }
902 }
903 }
904
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800905 // Add a transport to our set of available backends. If 'transport' is null, this
906 // is an unregistration, and the transport's entry is removed from our bookkeeping.
Christopher Tate91717492009-06-26 21:07:13 -0700907 private void registerTransport(String name, IBackupTransport transport) {
908 synchronized (mTransports) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800909 if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport);
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800910 if (transport != null) {
911 mTransports.put(name, transport);
912 } else {
913 mTransports.remove(name);
Christopher Tateb0dcaaf2010-01-29 16:27:04 -0800914 if ((mCurrentTransport != null) && mCurrentTransport.equals(name)) {
Christopher Tatedfa47b56e2009-12-22 16:01:32 -0800915 mCurrentTransport = null;
916 }
917 // Nothing further to do in the unregistration case
918 return;
919 }
Christopher Tate91717492009-06-26 21:07:13 -0700920 }
Christopher Tate4cc86e12009-09-21 19:36:51 -0700921
922 // If the init sentinel file exists, we need to be sure to perform the init
923 // as soon as practical. We also create the state directory at registration
924 // time to ensure it's present from the outset.
925 try {
926 String transportName = transport.transportDirName();
927 File stateDir = new File(mBaseStateDir, transportName);
928 stateDir.mkdirs();
929
930 File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
931 if (initSentinel.exists()) {
932 synchronized (mQueueLock) {
933 mPendingInits.add(transportName);
934
935 // TODO: pick a better starting time than now + 1 minute
936 long delay = 1000 * 60; // one minute, in milliseconds
937 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
938 System.currentTimeMillis() + delay, mRunInitIntent);
939 }
940 }
941 } catch (RemoteException e) {
942 // can't happen, the transport is local
943 }
Christopher Tate91717492009-06-26 21:07:13 -0700944 }
945
Christopher Tate3799bc22009-05-06 16:13:56 -0700946 // ----- Track installation/removal of packages -----
947 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
948 public void onReceive(Context context, Intent intent) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800949 if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
Christopher Tate3799bc22009-05-06 16:13:56 -0700950
Christopher Tate3799bc22009-05-06 16:13:56 -0700951 String action = intent.getAction();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800952 boolean replacing = false;
953 boolean added = false;
954 Bundle extras = intent.getExtras();
955 String pkgList[] = null;
956 if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
957 Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
958 Uri uri = intent.getData();
959 if (uri == null) {
960 return;
961 }
962 String pkgName = uri.getSchemeSpecificPart();
963 if (pkgName != null) {
964 pkgList = new String[] { pkgName };
965 }
966 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
967 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800968 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800969 added = true;
970 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800971 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800972 added = false;
973 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
974 }
975 if (pkgList == null || pkgList.length == 0) {
976 return;
977 }
978 if (added) {
Christopher Tate3799bc22009-05-06 16:13:56 -0700979 synchronized (mBackupParticipants) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800980 for (String pkgName : pkgList) {
981 if (replacing) {
982 // The package was just upgraded
983 updatePackageParticipantsLocked(pkgName);
984 } else {
985 // The package was just added
986 addPackageParticipantsLocked(pkgName);
987 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700988 }
989 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800990 } else {
991 if (replacing) {
Christopher Tate3799bc22009-05-06 16:13:56 -0700992 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
993 } else {
994 synchronized (mBackupParticipants) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800995 for (String pkgName : pkgList) {
996 removePackageParticipantsLocked(pkgName);
997 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700998 }
999 }
1000 }
1001 }
1002 };
1003
Dan Egnor87a02bc2009-06-17 02:30:10 -07001004 // ----- Track connection to GoogleBackupTransport service -----
1005 ServiceConnection mGoogleConnection = new ServiceConnection() {
1006 public void onServiceConnected(ComponentName name, IBinder service) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001007 if (DEBUG) Slog.v(TAG, "Connected to Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -07001008 mGoogleTransport = IBackupTransport.Stub.asInterface(service);
Christopher Tate91717492009-06-26 21:07:13 -07001009 registerTransport(name.flattenToShortString(), mGoogleTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -07001010 }
1011
1012 public void onServiceDisconnected(ComponentName name) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001013 if (DEBUG) Slog.v(TAG, "Disconnected from Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -07001014 mGoogleTransport = null;
Christopher Tate91717492009-06-26 21:07:13 -07001015 registerTransport(name.flattenToShortString(), null);
Dan Egnor87a02bc2009-06-17 02:30:10 -07001016 }
1017 };
1018
Christopher Tate181fafa2009-05-14 11:12:14 -07001019 // Add the backup agents in the given package to our set of known backup participants.
1020 // If 'packageName' is null, adds all backup agents in the whole system.
Christopher Tate3799bc22009-05-06 16:13:56 -07001021 void addPackageParticipantsLocked(String packageName) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001022 // Look for apps that define the android:backupAgent attribute
Joe Onorato8a9b2202010-02-26 18:56:32 -08001023 if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: " + packageName);
Dan Egnorefe52642009-06-24 00:16:33 -07001024 List<PackageInfo> targetApps = allAgentPackages();
Christopher Tate181fafa2009-05-14 11:12:14 -07001025 addPackageParticipantsLockedInner(packageName, targetApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001026 }
1027
Christopher Tate181fafa2009-05-14 11:12:14 -07001028 private void addPackageParticipantsLockedInner(String packageName,
Dan Egnorefe52642009-06-24 00:16:33 -07001029 List<PackageInfo> targetPkgs) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001030 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001031 Slog.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
Dan Egnorefe52642009-06-24 00:16:33 -07001032 for (PackageInfo p : targetPkgs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001033 Slog.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName
Christopher Tate5e1ab332009-09-01 20:32:49 -07001034 + " uid=" + p.applicationInfo.uid
1035 + " killAfterRestore="
1036 + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false")
Christopher Tate5e1ab332009-09-01 20:32:49 -07001037 );
Christopher Tate181fafa2009-05-14 11:12:14 -07001038 }
1039 }
1040
Dan Egnorefe52642009-06-24 00:16:33 -07001041 for (PackageInfo pkg : targetPkgs) {
1042 if (packageName == null || pkg.packageName.equals(packageName)) {
1043 int uid = pkg.applicationInfo.uid;
Christopher Tate181fafa2009-05-14 11:12:14 -07001044 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001045 if (set == null) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001046 set = new HashSet<ApplicationInfo>();
Christopher Tate3799bc22009-05-06 16:13:56 -07001047 mBackupParticipants.put(uid, set);
1048 }
Dan Egnorefe52642009-06-24 00:16:33 -07001049 set.add(pkg.applicationInfo);
Christopher Tate73e02522009-07-15 14:18:26 -07001050
1051 // If we've never seen this app before, schedule a backup for it
1052 if (!mEverStoredApps.contains(pkg.packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001053 if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
Christopher Tate73e02522009-07-15 14:18:26 -07001054 + " never backed up; scheduling");
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001055 dataChangedImpl(pkg.packageName);
Christopher Tate73e02522009-07-15 14:18:26 -07001056 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001057 }
Christopher Tate487529a2009-04-29 14:03:25 -07001058 }
1059 }
1060
Christopher Tate6785dd82009-06-18 15:58:25 -07001061 // Remove the given package's entry from our known active set. If
1062 // 'packageName' is null, *all* participating apps will be removed.
Christopher Tate3799bc22009-05-06 16:13:56 -07001063 void removePackageParticipantsLocked(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001064 if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: " + packageName);
Christopher Tatec28083a2010-12-14 16:16:44 -08001065 List<String> allApps = new ArrayList<String>();
Christopher Tate181fafa2009-05-14 11:12:14 -07001066 if (packageName != null) {
Christopher Tatec28083a2010-12-14 16:16:44 -08001067 allApps.add(packageName);
Christopher Tate181fafa2009-05-14 11:12:14 -07001068 } else {
1069 // all apps with agents
Christopher Tatec28083a2010-12-14 16:16:44 -08001070 List<PackageInfo> knownPackages = allAgentPackages();
1071 for (PackageInfo pkg : knownPackages) {
1072 allApps.add(pkg.packageName);
1073 }
Christopher Tate181fafa2009-05-14 11:12:14 -07001074 }
1075 removePackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001076 }
1077
Joe Onorato8ad02812009-05-13 01:41:44 -04001078 private void removePackageParticipantsLockedInner(String packageName,
Christopher Tatec28083a2010-12-14 16:16:44 -08001079 List<String> allPackageNames) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001080 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001081 Slog.v(TAG, "removePackageParticipantsLockedInner (" + packageName
Christopher Tatec28083a2010-12-14 16:16:44 -08001082 + ") removing " + allPackageNames.size() + " entries");
1083 for (String p : allPackageNames) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001084 Slog.v(TAG, " - " + p);
Christopher Tate043dadc2009-06-02 16:11:00 -07001085 }
1086 }
Christopher Tatec28083a2010-12-14 16:16:44 -08001087 for (String pkg : allPackageNames) {
1088 if (packageName == null || pkg.equals(packageName)) {
1089 int uid = -1;
1090 try {
1091 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1092 uid = info.applicationInfo.uid;
1093 } catch (NameNotFoundException e) {
1094 // we don't know this package name, so just skip it for now
1095 continue;
1096 }
1097
Christopher Tate181fafa2009-05-14 11:12:14 -07001098 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001099 if (set != null) {
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001100 // Find the existing entry with the same package name, and remove it.
1101 // We can't just remove(app) because the instances are different.
1102 for (ApplicationInfo entry: set) {
Christopher Tatec28083a2010-12-14 16:16:44 -08001103 if (entry.packageName.equals(pkg)) {
1104 if (DEBUG) Slog.v(TAG, " removing participant " + pkg);
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001105 set.remove(entry);
Christopher Tatec28083a2010-12-14 16:16:44 -08001106 removeEverBackedUp(pkg);
Christopher Tatecd4ff2e2009-06-05 13:57:54 -07001107 break;
1108 }
1109 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001110 if (set.size() == 0) {
Dan Egnorefe52642009-06-24 00:16:33 -07001111 mBackupParticipants.delete(uid);
1112 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001113 }
1114 }
1115 }
1116 }
1117
Christopher Tate181fafa2009-05-14 11:12:14 -07001118 // Returns the set of all applications that define an android:backupAgent attribute
Christopher Tate73e02522009-07-15 14:18:26 -07001119 List<PackageInfo> allAgentPackages() {
Christopher Tate6785dd82009-06-18 15:58:25 -07001120 // !!! TODO: cache this and regenerate only when necessary
Dan Egnorefe52642009-06-24 00:16:33 -07001121 int flags = PackageManager.GET_SIGNATURES;
1122 List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
1123 int N = packages.size();
1124 for (int a = N-1; a >= 0; a--) {
Christopher Tate0749dcd2009-08-13 15:13:03 -07001125 PackageInfo pkg = packages.get(a);
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001126 try {
1127 ApplicationInfo app = pkg.applicationInfo;
1128 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
Christopher Tatea87240c2010-02-12 14:12:34 -08001129 || app.backupAgentName == null) {
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001130 packages.remove(a);
1131 }
1132 else {
1133 // we will need the shared library path, so look that up and store it here
1134 app = mPackageManager.getApplicationInfo(pkg.packageName,
1135 PackageManager.GET_SHARED_LIBRARY_FILES);
1136 pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
1137 }
1138 } catch (NameNotFoundException e) {
Dan Egnorefe52642009-06-24 00:16:33 -07001139 packages.remove(a);
Christopher Tate181fafa2009-05-14 11:12:14 -07001140 }
1141 }
Dan Egnorefe52642009-06-24 00:16:33 -07001142 return packages;
Christopher Tate181fafa2009-05-14 11:12:14 -07001143 }
Christopher Tateaa088442009-06-16 18:25:46 -07001144
Christopher Tate3799bc22009-05-06 16:13:56 -07001145 // Reset the given package's known backup participants. Unlike add/remove, the update
1146 // action cannot be passed a null package name.
1147 void updatePackageParticipantsLocked(String packageName) {
1148 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001149 Slog.e(TAG, "updatePackageParticipants called with null package name");
Christopher Tate3799bc22009-05-06 16:13:56 -07001150 return;
1151 }
Joe Onorato8a9b2202010-02-26 18:56:32 -08001152 if (DEBUG) Slog.v(TAG, "updatePackageParticipantsLocked: " + packageName);
Christopher Tate3799bc22009-05-06 16:13:56 -07001153
1154 // brute force but small code size
Dan Egnorefe52642009-06-24 00:16:33 -07001155 List<PackageInfo> allApps = allAgentPackages();
Christopher Tatec28083a2010-12-14 16:16:44 -08001156 List<String> allAppNames = new ArrayList<String>();
1157 for (PackageInfo pkg : allApps) {
1158 allAppNames.add(pkg.packageName);
1159 }
1160 removePackageParticipantsLockedInner(packageName, allAppNames);
Christopher Tate181fafa2009-05-14 11:12:14 -07001161 addPackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -07001162 }
1163
Christopher Tate84725812010-02-04 15:52:40 -08001164 // Called from the backup task: record that the given app has been successfully
Christopher Tate73e02522009-07-15 14:18:26 -07001165 // backed up at least once
1166 void logBackupComplete(String packageName) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001167 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
1168
1169 synchronized (mEverStoredApps) {
1170 if (!mEverStoredApps.add(packageName)) return;
1171
1172 RandomAccessFile out = null;
1173 try {
1174 out = new RandomAccessFile(mEverStored, "rws");
1175 out.seek(out.length());
1176 out.writeUTF(packageName);
1177 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001178 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -07001179 } finally {
1180 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tate73e02522009-07-15 14:18:26 -07001181 }
1182 }
1183 }
1184
Christopher Tatee97e8072009-07-15 16:45:50 -07001185 // Remove our awareness of having ever backed up the given package
1186 void removeEverBackedUp(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001187 if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:");
Christopher Tatee97e8072009-07-15 16:45:50 -07001188
Dan Egnor852f8e42009-09-30 11:20:45 -07001189 synchronized (mEverStoredApps) {
1190 // Rewrite the file and rename to overwrite. If we reboot in the middle,
1191 // we'll recognize on initialization time that the package no longer
1192 // exists and fix it up then.
1193 File tempKnownFile = new File(mBaseStateDir, "processed.new");
1194 RandomAccessFile known = null;
1195 try {
1196 known = new RandomAccessFile(tempKnownFile, "rws");
1197 mEverStoredApps.remove(packageName);
1198 for (String s : mEverStoredApps) {
1199 known.writeUTF(s);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001200 if (DEBUG) Slog.v(TAG, " " + s);
Christopher Tatee97e8072009-07-15 16:45:50 -07001201 }
Dan Egnor852f8e42009-09-30 11:20:45 -07001202 known.close();
1203 known = null;
1204 if (!tempKnownFile.renameTo(mEverStored)) {
1205 throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
1206 }
1207 } catch (IOException e) {
1208 // Bad: we couldn't create the new copy. For safety's sake we
1209 // abandon the whole process and remove all what's-backed-up
1210 // state entirely, meaning we'll force a backup pass for every
1211 // participant on the next boot or [re]install.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001212 Slog.w(TAG, "Error rewriting " + mEverStored, e);
Dan Egnor852f8e42009-09-30 11:20:45 -07001213 mEverStoredApps.clear();
1214 tempKnownFile.delete();
1215 mEverStored.delete();
1216 } finally {
1217 try { if (known != null) known.close(); } catch (IOException e) {}
Christopher Tatee97e8072009-07-15 16:45:50 -07001218 }
1219 }
1220 }
1221
Christopher Tateb49ceb32010-02-08 16:22:24 -08001222 // Persistently record the current and ancestral backup tokens as well
1223 // as the set of packages with data [supposedly] available in the
1224 // ancestral dataset.
Christopher Tate84725812010-02-04 15:52:40 -08001225 void writeRestoreTokens() {
1226 try {
1227 RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
Christopher Tateb49ceb32010-02-08 16:22:24 -08001228
1229 // First, the version number of this record, for futureproofing
1230 af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
1231
1232 // Write the ancestral and current tokens
Christopher Tate84725812010-02-04 15:52:40 -08001233 af.writeLong(mAncestralToken);
1234 af.writeLong(mCurrentToken);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001235
1236 // Now write the set of ancestral packages
1237 if (mAncestralPackages == null) {
1238 af.writeInt(-1);
1239 } else {
1240 af.writeInt(mAncestralPackages.size());
Joe Onorato8a9b2202010-02-26 18:56:32 -08001241 if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
Christopher Tateb49ceb32010-02-08 16:22:24 -08001242 for (String pkgName : mAncestralPackages) {
1243 af.writeUTF(pkgName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001244 if (DEBUG) Slog.v(TAG, " " + pkgName);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001245 }
1246 }
Christopher Tate84725812010-02-04 15:52:40 -08001247 af.close();
1248 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001249 Slog.w(TAG, "Unable to write token file:", e);
Christopher Tate84725812010-02-04 15:52:40 -08001250 }
1251 }
1252
Dan Egnor87a02bc2009-06-17 02:30:10 -07001253 // Return the given transport
Christopher Tate91717492009-06-26 21:07:13 -07001254 private IBackupTransport getTransport(String transportName) {
1255 synchronized (mTransports) {
1256 IBackupTransport transport = mTransports.get(transportName);
1257 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001258 Slog.w(TAG, "Requested unavailable transport: " + transportName);
Christopher Tate91717492009-06-26 21:07:13 -07001259 }
1260 return transport;
Christopher Tate8c850b72009-06-07 19:33:20 -07001261 }
Christopher Tate8c850b72009-06-07 19:33:20 -07001262 }
1263
Christopher Tatedf01dea2009-06-09 20:45:02 -07001264 // fire off a backup agent, blocking until it attaches or times out
1265 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
1266 IBackupAgent agent = null;
1267 synchronized(mAgentConnectLock) {
1268 mConnecting = true;
1269 mConnectedAgent = null;
1270 try {
1271 if (mActivityManager.bindBackupAgent(app, mode)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001272 Slog.d(TAG, "awaiting agent for " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001273
1274 // success; wait for the agent to arrive
Christopher Tate75a99702011-05-18 16:28:19 -07001275 // only wait 10 seconds for the bind to happen
Christopher Tatec7b31e32009-06-10 15:49:30 -07001276 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1277 while (mConnecting && mConnectedAgent == null
1278 && (System.currentTimeMillis() < timeoutMark)) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07001279 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001280 mAgentConnectLock.wait(5000);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001281 } catch (InterruptedException e) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001282 // just bail
Christopher Tatedf01dea2009-06-09 20:45:02 -07001283 return null;
1284 }
1285 }
1286
1287 // if we timed out with no connect, abort and move on
1288 if (mConnecting == true) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001289 Slog.w(TAG, "Timeout waiting for agent " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001290 return null;
1291 }
1292 agent = mConnectedAgent;
1293 }
1294 } catch (RemoteException e) {
1295 // can't happen
1296 }
1297 }
1298 return agent;
1299 }
1300
Christopher Tatec7b31e32009-06-10 15:49:30 -07001301 // clear an application's data, blocking until the operation completes or times out
1302 void clearApplicationDataSynchronous(String packageName) {
Christopher Tatef7c886b2009-06-26 15:34:09 -07001303 // Don't wipe packages marked allowClearUserData=false
1304 try {
1305 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1306 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001307 if (DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
Christopher Tatef7c886b2009-06-26 15:34:09 -07001308 + packageName);
1309 return;
1310 }
1311 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001312 Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
Christopher Tatef7c886b2009-06-26 15:34:09 -07001313 return;
1314 }
1315
Christopher Tatec7b31e32009-06-10 15:49:30 -07001316 ClearDataObserver observer = new ClearDataObserver();
1317
1318 synchronized(mClearDataLock) {
1319 mClearingData = true;
Christopher Tate9dfdac52009-08-06 14:57:53 -07001320 try {
1321 mActivityManager.clearApplicationUserData(packageName, observer);
1322 } catch (RemoteException e) {
1323 // can't happen because the activity manager is in this process
1324 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001325
1326 // only wait 10 seconds for the clear data to happen
1327 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1328 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
1329 try {
1330 mClearDataLock.wait(5000);
1331 } catch (InterruptedException e) {
1332 // won't happen, but still.
1333 mClearingData = false;
1334 }
1335 }
1336 }
1337 }
1338
1339 class ClearDataObserver extends IPackageDataObserver.Stub {
Dan Egnor852f8e42009-09-30 11:20:45 -07001340 public void onRemoveCompleted(String packageName, boolean succeeded) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001341 synchronized(mClearDataLock) {
1342 mClearingData = false;
Christopher Tatef68eb502009-06-16 11:02:01 -07001343 mClearDataLock.notifyAll();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001344 }
1345 }
1346 }
1347
Christopher Tate1bb69062010-02-19 17:02:12 -08001348 // Get the restore-set token for the best-available restore set for this package:
1349 // the active set if possible, else the ancestral one. Returns zero if none available.
1350 long getAvailableRestoreToken(String packageName) {
1351 long token = mAncestralToken;
1352 synchronized (mQueueLock) {
1353 if (mEverStoredApps.contains(packageName)) {
1354 token = mCurrentToken;
1355 }
1356 }
1357 return token;
1358 }
1359
Christopher Tate44a27902010-01-27 17:15:49 -08001360 // -----
1361 // Utility methods used by the asynchronous-with-timeout backup/restore operations
1362 boolean waitUntilOperationComplete(int token) {
1363 int finalState = OP_PENDING;
1364 synchronized (mCurrentOpLock) {
1365 try {
1366 while ((finalState = mCurrentOperations.get(token, OP_TIMEOUT)) == OP_PENDING) {
1367 try {
1368 mCurrentOpLock.wait();
1369 } catch (InterruptedException e) {}
1370 }
1371 } catch (IndexOutOfBoundsException e) {
1372 // the operation has been mysteriously cleared from our
1373 // bookkeeping -- consider this a success and ignore it.
1374 }
1375 }
1376 mBackupHandler.removeMessages(MSG_TIMEOUT);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001377 if (DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001378 + " complete: finalState=" + finalState);
Christopher Tate44a27902010-01-27 17:15:49 -08001379 return finalState == OP_ACKNOWLEDGED;
1380 }
1381
1382 void prepareOperationTimeout(int token, long interval) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001383 if (DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001384 + " interval=" + interval);
Christopher Tate4a627c72011-04-01 14:43:32 -07001385 synchronized (mCurrentOpLock) {
1386 mCurrentOperations.put(token, OP_PENDING);
1387 Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
1388 mBackupHandler.sendMessageDelayed(msg, interval);
1389 }
Christopher Tate44a27902010-01-27 17:15:49 -08001390 }
1391
Christopher Tate043dadc2009-06-02 16:11:00 -07001392 // ----- Back up a set of applications via a worker thread -----
1393
Christopher Tate44a27902010-01-27 17:15:49 -08001394 class PerformBackupTask implements Runnable {
Christopher Tate043dadc2009-06-02 16:11:00 -07001395 private static final String TAG = "PerformBackupThread";
Christopher Tateaa088442009-06-16 18:25:46 -07001396 IBackupTransport mTransport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001397 ArrayList<BackupRequest> mQueue;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001398 File mStateDir;
Christopher Tatecde87f42009-06-12 12:55:53 -07001399 File mJournal;
Christopher Tate043dadc2009-06-02 16:11:00 -07001400
Christopher Tate44a27902010-01-27 17:15:49 -08001401 public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue,
Christopher Tatecde87f42009-06-12 12:55:53 -07001402 File journal) {
Christopher Tateaa088442009-06-16 18:25:46 -07001403 mTransport = transport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001404 mQueue = queue;
Christopher Tatecde87f42009-06-12 12:55:53 -07001405 mJournal = journal;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001406
1407 try {
1408 mStateDir = new File(mBaseStateDir, transport.transportDirName());
1409 } catch (RemoteException e) {
1410 // can't happen; the transport is local
1411 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001412 }
1413
Christopher Tate043dadc2009-06-02 16:11:00 -07001414 public void run() {
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001415 int status = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001416 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08001417 if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
Christopher Tate043dadc2009-06-02 16:11:00 -07001418
Christopher Tate79588342009-06-30 16:11:49 -07001419 // Backups run at background priority
1420 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
1421
Christopher Tate043dadc2009-06-02 16:11:00 -07001422 try {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001423 EventLog.writeEvent(EventLogTags.BACKUP_START, mTransport.transportDirName());
Dan Egnor01445162009-09-21 17:04:05 -07001424
Dan Egnor852f8e42009-09-30 11:20:45 -07001425 // If we haven't stored package manager metadata yet, we must init the transport.
1426 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
1427 if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001428 Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
Dan Egnor852f8e42009-09-30 11:20:45 -07001429 resetBackupState(mStateDir); // Just to make sure.
Dan Egnor01445162009-09-21 17:04:05 -07001430 status = mTransport.initializeDevice();
Dan Egnor726247c2009-09-29 19:12:31 -07001431 if (status == BackupConstants.TRANSPORT_OK) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001432 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07001433 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001434 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001435 Slog.e(TAG, "Transport error in initializeDevice()");
Dan Egnor726247c2009-09-29 19:12:31 -07001436 }
Dan Egnor01445162009-09-21 17:04:05 -07001437 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001438
1439 // The package manager doesn't have a proper <application> etc, but since
1440 // it's running here in the system process we can just set up its agent
1441 // directly and use a synthetic BackupRequest. We always run this pass
1442 // because it's cheap and this way we guarantee that we don't get out of
1443 // step even if we're selecting among various transports at run time.
Dan Egnor01445162009-09-21 17:04:05 -07001444 if (status == BackupConstants.TRANSPORT_OK) {
1445 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
1446 mPackageManager, allAgentPackages());
Christopher Tate4a627c72011-04-01 14:43:32 -07001447 BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
Dan Egnor01445162009-09-21 17:04:05 -07001448 pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
1449 status = processOneBackup(pmRequest,
1450 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
1451 }
Christopher Tate90967f42009-09-20 15:28:33 -07001452
Dan Egnor01445162009-09-21 17:04:05 -07001453 if (status == BackupConstants.TRANSPORT_OK) {
1454 // Now run all the backups in our queue
1455 status = doQueuedBackups(mTransport);
1456 }
1457
1458 if (status == BackupConstants.TRANSPORT_OK) {
1459 // Tell the transport to finish everything it has buffered
1460 status = mTransport.finishBackup();
1461 if (status == BackupConstants.TRANSPORT_OK) {
1462 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001463 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, mQueue.size(), millis);
Dan Egnor01445162009-09-21 17:04:05 -07001464 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001465 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(finish)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001466 Slog.e(TAG, "Transport error in finishBackup()");
Dan Egnor01445162009-09-21 17:04:05 -07001467 }
1468 }
1469
Dan Egnor01445162009-09-21 17:04:05 -07001470 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Christopher Tated55e18a2009-09-21 10:12:59 -07001471 // The backend reports that our dataset has been wiped. We need to
1472 // reset all of our bookkeeping and instead run a new backup pass for
Christopher Tatec2af5d32010-02-02 15:18:58 -08001473 // everything.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001474 EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
Christopher Tated55e18a2009-09-21 10:12:59 -07001475 resetBackupState(mStateDir);
Dan Egnorbb9001c2009-07-27 12:20:13 -07001476 }
1477 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001478 Slog.e(TAG, "Error in backup thread", e);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001479 status = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001480 } finally {
Christopher Tate84725812010-02-04 15:52:40 -08001481 // If everything actually went through and this is the first time we've
1482 // done a backup, we can now record what the current backup dataset token
1483 // is.
Christopher Tate29505552010-06-24 15:58:01 -07001484 if ((mCurrentToken == 0) && (status == BackupConstants.TRANSPORT_OK)) {
Christopher Tate84725812010-02-04 15:52:40 -08001485 try {
1486 mCurrentToken = mTransport.getCurrentRestoreSet();
1487 } catch (RemoteException e) { /* cannot happen */ }
1488 writeRestoreTokens();
1489 }
1490
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001491 // If things went wrong, we need to re-stage the apps we had expected
1492 // to be backing up in this pass. This journals the package names in
1493 // the current active pending-backup file, not in the we are holding
1494 // here in mJournal.
1495 if (status != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001496 Slog.w(TAG, "Backup pass unsuccessful, restaging");
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001497 for (BackupRequest req : mQueue) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001498 dataChangedImpl(req.appInfo.packageName);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001499 }
Christopher Tate21ab6a52009-09-24 18:01:46 -07001500
1501 // We also want to reset the backup schedule based on whatever
1502 // the transport suggests by way of retry/backoff time.
1503 try {
1504 startBackupAlarmsLocked(mTransport.requestBackupTime());
1505 } catch (RemoteException e) { /* cannot happen */ }
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001506 }
1507
1508 // Either backup was successful, in which case we of course do not need
1509 // this pass's journal any more; or it failed, in which case we just
1510 // re-enqueued all of these packages in the current active journal.
1511 // Either way, we no longer need this pass's journal.
Dan Egnor852f8e42009-09-30 11:20:45 -07001512 if (mJournal != null && !mJournal.delete()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001513 Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
Christopher Tateb03b3bb2009-09-22 11:14:17 -07001514 }
1515
Christopher Tatec2af5d32010-02-02 15:18:58 -08001516 // Only once we're entirely finished do we release the wakelock
Dan Egnor852f8e42009-09-30 11:20:45 -07001517 if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001518 backupNow();
1519 }
1520
Dan Egnorbb9001c2009-07-27 12:20:13 -07001521 mWakelock.release();
Christopher Tatecde87f42009-06-12 12:55:53 -07001522 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001523 }
1524
Dan Egnor01445162009-09-21 17:04:05 -07001525 private int doQueuedBackups(IBackupTransport transport) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001526 for (BackupRequest request : mQueue) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001527 Slog.d(TAG, "starting agent for backup of " + request);
Christopher Tate043dadc2009-06-02 16:11:00 -07001528
Christopher Tatec28083a2010-12-14 16:16:44 -08001529 // Verify that the requested app exists; it might be something that
1530 // requested a backup but was then uninstalled. The request was
1531 // journalled and rather than tamper with the journal it's safer
1532 // to sanity-check here.
1533 try {
1534 mPackageManager.getPackageInfo(request.appInfo.packageName, 0);
1535 } catch (NameNotFoundException e) {
1536 Slog.d(TAG, "Package does not exist; skipping");
1537 continue;
1538 }
1539
Christopher Tate043dadc2009-06-02 16:11:00 -07001540 IBackupAgent agent = null;
Christopher Tate043dadc2009-06-02 16:11:00 -07001541 try {
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001542 mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
Christopher Tate4a627c72011-04-01 14:43:32 -07001543 agent = bindToAgentSynchronous(request.appInfo,
1544 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001545 if (agent != null) {
Dan Egnor01445162009-09-21 17:04:05 -07001546 int result = processOneBackup(request, agent, transport);
1547 if (result != BackupConstants.TRANSPORT_OK) return result;
Christopher Tate043dadc2009-06-02 16:11:00 -07001548 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001549 } catch (SecurityException ex) {
1550 // Try for the next one.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001551 Slog.d(TAG, "error in bind/backup", ex);
Dan Egnor01445162009-09-21 17:04:05 -07001552 } finally {
1553 try { // unbind even on timeout, just in case
1554 mActivityManager.unbindBackupAgent(request.appInfo);
1555 } catch (RemoteException e) {}
Christopher Tate043dadc2009-06-02 16:11:00 -07001556 }
1557 }
Dan Egnor01445162009-09-21 17:04:05 -07001558
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001559 mWakelock.setWorkSource(null);
1560
Dan Egnor01445162009-09-21 17:04:05 -07001561 return BackupConstants.TRANSPORT_OK;
Christopher Tate043dadc2009-06-02 16:11:00 -07001562 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001563
Dan Egnor01445162009-09-21 17:04:05 -07001564 private int processOneBackup(BackupRequest request, IBackupAgent agent,
1565 IBackupTransport transport) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001566 final String packageName = request.appInfo.packageName;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001567 if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001568
Dan Egnorbb9001c2009-07-27 12:20:13 -07001569 File savedStateName = new File(mStateDir, packageName);
1570 File backupDataName = new File(mDataDir, packageName + ".data");
1571 File newStateName = new File(mStateDir, packageName + ".new");
1572
1573 ParcelFileDescriptor savedState = null;
1574 ParcelFileDescriptor backupData = null;
1575 ParcelFileDescriptor newState = null;
1576
1577 PackageInfo packInfo;
Christopher Tate4a627c72011-04-01 14:43:32 -07001578 final int token = generateToken();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001579 try {
1580 // Look up the package info & signatures. This is first so that if it
1581 // throws an exception, there's no file setup yet that would need to
1582 // be unraveled.
Christopher Tateabce4e82009-06-18 18:35:32 -07001583 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
1584 // The metadata 'package' is synthetic
1585 packInfo = new PackageInfo();
1586 packInfo.packageName = packageName;
1587 } else {
1588 packInfo = mPackageManager.getPackageInfo(packageName,
Christopher Tatec7b31e32009-06-10 15:49:30 -07001589 PackageManager.GET_SIGNATURES);
Christopher Tateabce4e82009-06-18 18:35:32 -07001590 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001591
Christopher Tatec7b31e32009-06-10 15:49:30 -07001592 // In a full backup, we pass a null ParcelFileDescriptor as
Christopher Tate4a627c72011-04-01 14:43:32 -07001593 // the saved-state "file". This is by definition an incremental,
1594 // so we build a saved state file to pass.
1595 savedState = ParcelFileDescriptor.open(savedStateName,
1596 ParcelFileDescriptor.MODE_READ_ONLY |
1597 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
Christopher Tatec7b31e32009-06-10 15:49:30 -07001598
Dan Egnorbb9001c2009-07-27 12:20:13 -07001599 backupData = ParcelFileDescriptor.open(backupDataName,
1600 ParcelFileDescriptor.MODE_READ_WRITE |
1601 ParcelFileDescriptor.MODE_CREATE |
1602 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001603
Dan Egnorbb9001c2009-07-27 12:20:13 -07001604 newState = ParcelFileDescriptor.open(newStateName,
1605 ParcelFileDescriptor.MODE_READ_WRITE |
1606 ParcelFileDescriptor.MODE_CREATE |
1607 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07001608
Christopher Tate44a27902010-01-27 17:15:49 -08001609 // Initiate the target's backup pass
1610 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001611 agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
Christopher Tate44a27902010-01-27 17:15:49 -08001612 boolean success = waitUntilOperationComplete(token);
1613
1614 if (!success) {
1615 // timeout -- bail out into the failed-transaction logic
1616 throw new RuntimeException("Backup timeout");
1617 }
1618
Dan Egnorbb9001c2009-07-27 12:20:13 -07001619 logBackupComplete(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08001620 if (DEBUG) Slog.v(TAG, "doBackup() success");
Christopher Tatec7b31e32009-06-10 15:49:30 -07001621 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001622 Slog.e(TAG, "Error backing up " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001623 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07001624 backupDataName.delete();
1625 newStateName.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -07001626 return BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001627 } finally {
1628 try { if (savedState != null) savedState.close(); } catch (IOException e) {}
1629 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
1630 try { if (newState != null) newState.close(); } catch (IOException e) {}
1631 savedState = backupData = newState = null;
Christopher Tate44a27902010-01-27 17:15:49 -08001632 synchronized (mCurrentOpLock) {
1633 mCurrentOperations.clear();
1634 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001635 }
1636
1637 // Now propagate the newly-backed-up data to the transport
Dan Egnor01445162009-09-21 17:04:05 -07001638 int result = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001639 try {
1640 int size = (int) backupDataName.length();
1641 if (size > 0) {
Dan Egnor01445162009-09-21 17:04:05 -07001642 if (result == BackupConstants.TRANSPORT_OK) {
1643 backupData = ParcelFileDescriptor.open(backupDataName,
1644 ParcelFileDescriptor.MODE_READ_ONLY);
1645 result = transport.performBackup(packInfo, backupData);
1646 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001647
Dan Egnor83861e72009-09-17 16:17:55 -07001648 // TODO - We call finishBackup() for each application backed up, because
1649 // we need to know now whether it succeeded or failed. Instead, we should
1650 // hold off on finishBackup() until the end, which implies holding off on
1651 // renaming *all* the output state files (see below) until that happens.
1652
Dan Egnor01445162009-09-21 17:04:05 -07001653 if (result == BackupConstants.TRANSPORT_OK) {
1654 result = transport.finishBackup();
Dan Egnor83861e72009-09-17 16:17:55 -07001655 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001656 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001657 if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
Dan Egnorbb9001c2009-07-27 12:20:13 -07001658 }
1659
1660 // After successful transport, delete the now-stale data
1661 // and juggle the files so that next time we supply the agent
1662 // with the new state file it just created.
Dan Egnor01445162009-09-21 17:04:05 -07001663 if (result == BackupConstants.TRANSPORT_OK) {
1664 backupDataName.delete();
1665 newStateName.renameTo(savedStateName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001666 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, packageName, size);
Dan Egnor01445162009-09-21 17:04:05 -07001667 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001668 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
Dan Egnor01445162009-09-21 17:04:05 -07001669 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001670 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001671 Slog.e(TAG, "Transport error backing up " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001672 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
Dan Egnor01445162009-09-21 17:04:05 -07001673 result = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001674 } finally {
1675 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
Christopher Tatec7b31e32009-06-10 15:49:30 -07001676 }
Christopher Tated55e18a2009-09-21 10:12:59 -07001677
Dan Egnor01445162009-09-21 17:04:05 -07001678 return result;
Christopher Tatec7b31e32009-06-10 15:49:30 -07001679 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001680 }
1681
Christopher Tatedf01dea2009-06-09 20:45:02 -07001682
Christopher Tate4a627c72011-04-01 14:43:32 -07001683 // ----- Full backup to a file/socket -----
1684
1685 class PerformFullBackupTask implements Runnable {
1686 ParcelFileDescriptor mOutputFile;
Christopher Tate7926a692011-07-11 11:31:57 -07001687 DeflaterOutputStream mDeflater;
Christopher Tate4a627c72011-04-01 14:43:32 -07001688 IFullBackupRestoreObserver mObserver;
1689 boolean mIncludeApks;
1690 boolean mIncludeShared;
1691 boolean mAllApps;
1692 String[] mPackages;
1693 AtomicBoolean mLatchObject;
1694 File mFilesDir;
1695 File mManifestFile;
1696
Christopher Tate7926a692011-07-11 11:31:57 -07001697 class FullBackupRunner implements Runnable {
1698 PackageInfo mPackage;
1699 IBackupAgent mAgent;
1700 ParcelFileDescriptor mPipe;
1701 int mToken;
1702 boolean mSendApk;
1703
1704 FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
1705 int token, boolean sendApk) throws IOException {
1706 mPackage = pack;
1707 mAgent = agent;
1708 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
1709 mToken = token;
1710 mSendApk = sendApk;
1711 }
1712
1713 @Override
1714 public void run() {
1715 try {
1716 BackupDataOutput output = new BackupDataOutput(
1717 mPipe.getFileDescriptor());
1718
1719 if (DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
1720 writeAppManifest(mPackage, mManifestFile, mSendApk);
1721 FullBackup.backupToTar(mPackage.packageName, null, null,
1722 mFilesDir.getAbsolutePath(),
1723 mManifestFile.getAbsolutePath(),
1724 output);
1725
1726 if (mSendApk) {
1727 writeApkToBackup(mPackage, output);
1728 }
1729
1730 if (DEBUG) Slog.d(TAG, "Calling doFullBackup()");
1731 prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL);
1732 mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
1733 } catch (IOException e) {
1734 Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
1735 } catch (RemoteException e) {
1736 Slog.e(TAG, "Remote agent vanished during full backup of "
1737 + mPackage.packageName);
1738 } finally {
1739 try {
1740 mPipe.close();
1741 } catch (IOException e) {}
1742 }
1743 }
1744 }
1745
Christopher Tate4a627c72011-04-01 14:43:32 -07001746 PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
1747 boolean includeApks, boolean includeShared,
1748 boolean doAllApps, String[] packages, AtomicBoolean latch) {
1749 mOutputFile = fd;
1750 mObserver = observer;
1751 mIncludeApks = includeApks;
1752 mIncludeShared = includeShared;
1753 mAllApps = doAllApps;
1754 mPackages = packages;
1755 mLatchObject = latch;
1756
1757 mFilesDir = new File("/data/system");
1758 mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
1759 }
1760
1761 @Override
1762 public void run() {
1763 final List<PackageInfo> packagesToBackup;
1764
Christopher Tateb0628bf2011-06-02 15:08:13 -07001765 Slog.i(TAG, "--- Performing full-dataset backup ---");
Christopher Tate4a627c72011-04-01 14:43:32 -07001766 sendStartBackup();
1767
1768 // doAllApps supersedes the package set if any
1769 if (mAllApps) {
1770 packagesToBackup = mPackageManager.getInstalledPackages(
1771 PackageManager.GET_SIGNATURES);
1772 } else {
1773 packagesToBackup = new ArrayList<PackageInfo>();
1774 for (String pkgName : mPackages) {
1775 try {
1776 packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
1777 PackageManager.GET_SIGNATURES));
1778 } catch (NameNotFoundException e) {
1779 Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
1780 }
1781 }
1782 }
1783
Christopher Tatea858cb02011-06-03 12:27:51 -07001784 // Cull any packages that have indicated that backups are not permitted.
1785 for (int i = 0; i < packagesToBackup.size(); ) {
1786 PackageInfo info = packagesToBackup.get(i);
1787 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
1788 packagesToBackup.remove(i);
1789 } else {
1790 i++;
1791 }
1792 }
1793
Christopher Tate7926a692011-07-11 11:31:57 -07001794 // Set up the compression stage
1795 FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
1796 Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
1797 DeflaterOutputStream out = new DeflaterOutputStream(ofstream, deflater, true);
1798
1799 // !!! TODO: if using encryption, set up the encryption stage
1800 // and emit the tar header stating the password salt.
1801
Christopher Tate4a627c72011-04-01 14:43:32 -07001802 PackageInfo pkg = null;
1803 try {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001804 // Now back up the app data via the agent mechanism
Christopher Tate4a627c72011-04-01 14:43:32 -07001805 int N = packagesToBackup.size();
1806 for (int i = 0; i < N; i++) {
1807 pkg = packagesToBackup.get(i);
Christopher Tate7926a692011-07-11 11:31:57 -07001808 backupOnePackage(pkg, out);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001809 }
Christopher Tate4a627c72011-04-01 14:43:32 -07001810
Christopher Tateb0628bf2011-06-02 15:08:13 -07001811 // Finally, shared storage if requested
1812 if (mIncludeShared) {
1813 backupSharedStorage();
Christopher Tate4a627c72011-04-01 14:43:32 -07001814 }
1815 } catch (RemoteException e) {
1816 Slog.e(TAG, "App died during full backup");
1817 } finally {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001818 tearDown(pkg);
Christopher Tate4a627c72011-04-01 14:43:32 -07001819 try {
Christopher Tate7926a692011-07-11 11:31:57 -07001820 out.close();
Christopher Tate4a627c72011-04-01 14:43:32 -07001821 mOutputFile.close();
1822 } catch (IOException e) {
1823 /* nothing we can do about this */
1824 }
1825 synchronized (mCurrentOpLock) {
1826 mCurrentOperations.clear();
1827 }
1828 synchronized (mLatchObject) {
1829 mLatchObject.set(true);
1830 mLatchObject.notifyAll();
1831 }
1832 sendEndBackup();
1833 mWakelock.release();
1834 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
1835 }
1836 }
1837
Christopher Tate7926a692011-07-11 11:31:57 -07001838 private void backupOnePackage(PackageInfo pkg, DeflaterOutputStream out)
1839 throws RemoteException {
Christopher Tateb0628bf2011-06-02 15:08:13 -07001840 Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
1841
1842 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
1843 IApplicationThread.BACKUP_MODE_FULL);
1844 if (agent != null) {
Christopher Tate7926a692011-07-11 11:31:57 -07001845 ParcelFileDescriptor[] pipes = null;
Christopher Tateb0628bf2011-06-02 15:08:13 -07001846 try {
Christopher Tate7926a692011-07-11 11:31:57 -07001847 pipes = ParcelFileDescriptor.createPipe();
1848
Christopher Tateb0628bf2011-06-02 15:08:13 -07001849 ApplicationInfo app = pkg.applicationInfo;
Christopher Tate79ec80d2011-06-24 14:58:49 -07001850 final boolean sendApk = mIncludeApks
Christopher Tateb0628bf2011-06-02 15:08:13 -07001851 && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
1852 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
1853 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
1854
1855 sendOnBackupPackage(pkg.packageName);
1856
Christopher Tate7926a692011-07-11 11:31:57 -07001857 final int token = generateToken();
1858 FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
1859 token, sendApk);
1860 pipes[1].close(); // the runner has dup'd it
1861 pipes[1] = null;
1862 Thread t = new Thread(runner);
1863 t.start();
Christopher Tateb0628bf2011-06-02 15:08:13 -07001864
Christopher Tate7926a692011-07-11 11:31:57 -07001865 // Now pull data from the app and stuff it into the compressor
1866 try {
1867 FileInputStream raw = new FileInputStream(pipes[0].getFileDescriptor());
1868 DataInputStream in = new DataInputStream(raw);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001869
Christopher Tate7926a692011-07-11 11:31:57 -07001870 byte[] buffer = new byte[16 * 1024];
1871 int chunkTotal;
1872 while ((chunkTotal = in.readInt()) > 0) {
1873 while (chunkTotal > 0) {
1874 int toRead = (chunkTotal > buffer.length)
1875 ? buffer.length : chunkTotal;
1876 int nRead = in.read(buffer, 0, toRead);
1877 out.write(buffer, 0, nRead);
1878 chunkTotal -= nRead;
1879 }
1880 }
1881 } catch (IOException e) {
1882 Slog.i(TAG, "Caught exception reading from agent", e);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001883 }
1884
Christopher Tateb0628bf2011-06-02 15:08:13 -07001885 if (!waitUntilOperationComplete(token)) {
1886 Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
1887 } else {
Christopher Tate7926a692011-07-11 11:31:57 -07001888 if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001889 }
Christopher Tate7926a692011-07-11 11:31:57 -07001890
Christopher Tateb0628bf2011-06-02 15:08:13 -07001891 } catch (IOException e) {
1892 Slog.e(TAG, "Error backing up " + pkg.packageName, e);
Christopher Tate7926a692011-07-11 11:31:57 -07001893 } finally {
1894 try {
1895 if (pipes != null) {
1896 if (pipes[0] != null) pipes[0].close();
1897 if (pipes[1] != null) pipes[1].close();
1898 }
1899
1900 // Apply a full sync/flush after each application's data
1901 out.flush();
1902 } catch (IOException e) {
1903 Slog.w(TAG, "Error bringing down backup stack");
1904 }
Christopher Tateb0628bf2011-06-02 15:08:13 -07001905 }
1906 } else {
1907 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
1908 }
1909 tearDown(pkg);
1910 }
1911
Christopher Tate79ec80d2011-06-24 14:58:49 -07001912 private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
1913 // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
1914 final String appSourceDir = pkg.applicationInfo.sourceDir;
1915 final String apkDir = new File(appSourceDir).getParent();
1916 FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
1917 apkDir, appSourceDir, output);
1918
1919 // Save associated .obb content if it exists and we did save the apk
1920 // check for .obb and save those too
1921 final File obbDir = Environment.getExternalStorageAppObbDirectory(pkg.packageName);
1922 if (obbDir != null) {
1923 if (DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
1924 File[] obbFiles = obbDir.listFiles();
1925 if (obbFiles != null) {
1926 final String obbDirName = obbDir.getAbsolutePath();
1927 for (File obb : obbFiles) {
1928 FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
1929 obbDirName, obb.getAbsolutePath(), output);
1930 }
1931 }
1932 }
1933 }
1934
Christopher Tateb0628bf2011-06-02 15:08:13 -07001935 private void backupSharedStorage() throws RemoteException {
1936 PackageInfo pkg = null;
1937 try {
1938 pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
1939 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
1940 IApplicationThread.BACKUP_MODE_FULL);
1941 if (agent != null) {
1942 sendOnBackupPackage("Shared storage");
1943
1944 final int token = generateToken();
1945 prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
Christopher Tate79ec80d2011-06-24 14:58:49 -07001946 agent.doFullBackup(mOutputFile, token, mBackupManagerBinder);
Christopher Tateb0628bf2011-06-02 15:08:13 -07001947 if (!waitUntilOperationComplete(token)) {
1948 Slog.e(TAG, "Full backup failed on shared storage");
1949 } else {
1950 if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
1951 }
1952 } else {
1953 Slog.e(TAG, "Could not bind to shared storage backup agent");
1954 }
1955 } catch (NameNotFoundException e) {
1956 Slog.e(TAG, "Shared storage backup package not found");
1957 } finally {
1958 tearDown(pkg);
1959 }
1960 }
1961
Christopher Tate4a627c72011-04-01 14:43:32 -07001962 private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
1963 throws IOException {
1964 // Manifest format. All data are strings ending in LF:
1965 // BACKUP_MANIFEST_VERSION, currently 1
1966 //
1967 // Version 1:
1968 // package name
1969 // package's versionCode
Christopher Tate75a99702011-05-18 16:28:19 -07001970 // platform versionCode
1971 // getInstallerPackageName() for this package (maybe empty)
1972 // boolean: "1" if archive includes .apk; any other string means not
Christopher Tate4a627c72011-04-01 14:43:32 -07001973 // number of signatures == N
1974 // N*: signature byte array in ascii format per Signature.toCharsString()
1975 StringBuilder builder = new StringBuilder(4096);
1976 StringBuilderPrinter printer = new StringBuilderPrinter(builder);
1977
1978 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
1979 printer.println(pkg.packageName);
1980 printer.println(Integer.toString(pkg.versionCode));
Christopher Tate75a99702011-05-18 16:28:19 -07001981 printer.println(Integer.toString(Build.VERSION.SDK_INT));
1982
1983 String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
1984 printer.println((installerName != null) ? installerName : "");
1985
Christopher Tate4a627c72011-04-01 14:43:32 -07001986 printer.println(withApk ? "1" : "0");
1987 if (pkg.signatures == null) {
1988 printer.println("0");
1989 } else {
1990 printer.println(Integer.toString(pkg.signatures.length));
1991 for (Signature sig : pkg.signatures) {
1992 printer.println(sig.toCharsString());
1993 }
1994 }
1995
1996 FileOutputStream outstream = new FileOutputStream(manifestFile);
Christopher Tate4a627c72011-04-01 14:43:32 -07001997 outstream.write(builder.toString().getBytes());
1998 outstream.close();
1999 }
2000
2001 private void tearDown(PackageInfo pkg) {
Christopher Tateb0628bf2011-06-02 15:08:13 -07002002 if (pkg != null) {
2003 final ApplicationInfo app = pkg.applicationInfo;
2004 if (app != null) {
2005 try {
2006 // unbind and tidy up even on timeout or failure, just in case
2007 mActivityManager.unbindBackupAgent(app);
Christopher Tate4a627c72011-04-01 14:43:32 -07002008
Christopher Tateb0628bf2011-06-02 15:08:13 -07002009 // The agent was running with a stub Application object, so shut it down.
2010 if (app.uid != Process.SYSTEM_UID) {
2011 if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
2012 mActivityManager.killApplicationProcess(app.processName, app.uid);
2013 } else {
2014 if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
2015 }
2016 } catch (RemoteException e) {
2017 Slog.d(TAG, "Lost app trying to shut down");
2018 }
Christopher Tate4a627c72011-04-01 14:43:32 -07002019 }
Christopher Tate4a627c72011-04-01 14:43:32 -07002020 }
2021 }
2022
2023 // wrappers for observer use
2024 void sendStartBackup() {
2025 if (mObserver != null) {
2026 try {
2027 mObserver.onStartBackup();
2028 } catch (RemoteException e) {
2029 Slog.w(TAG, "full backup observer went away: startBackup");
2030 mObserver = null;
2031 }
2032 }
2033 }
2034
2035 void sendOnBackupPackage(String name) {
2036 if (mObserver != null) {
2037 try {
2038 // TODO: use a more user-friendly name string
2039 mObserver.onBackupPackage(name);
2040 } catch (RemoteException e) {
2041 Slog.w(TAG, "full backup observer went away: backupPackage");
2042 mObserver = null;
2043 }
2044 }
2045 }
2046
2047 void sendEndBackup() {
2048 if (mObserver != null) {
2049 try {
2050 mObserver.onEndBackup();
2051 } catch (RemoteException e) {
2052 Slog.w(TAG, "full backup observer went away: endBackup");
2053 mObserver = null;
2054 }
2055 }
2056 }
2057 }
2058
2059
Christopher Tate75a99702011-05-18 16:28:19 -07002060 // ----- Full restore from a file/socket -----
2061
2062 // Description of a file in the restore datastream
2063 static class FileMetadata {
2064 String packageName; // name of the owning app
2065 String installerPackageName; // name of the market-type app that installed the owner
Christopher Tate79ec80d2011-06-24 14:58:49 -07002066 int type; // e.g. BackupAgent.TYPE_DIRECTORY
Christopher Tate75a99702011-05-18 16:28:19 -07002067 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN
2068 String path; // subpath within the semantic domain
2069 long mode; // e.g. 0666 (actually int)
2070 long mtime; // last mod time, UTC time_t (actually int)
2071 long size; // bytes of content
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002072
2073 @Override
2074 public String toString() {
2075 StringBuilder sb = new StringBuilder(128);
2076 sb.append("FileMetadata{");
2077 sb.append(packageName); sb.append(',');
2078 sb.append(type); sb.append(',');
2079 sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
2080 sb.append(size);
2081 sb.append('}');
2082 return sb.toString();
2083 }
Christopher Tate75a99702011-05-18 16:28:19 -07002084 }
2085
2086 enum RestorePolicy {
2087 IGNORE,
2088 ACCEPT,
2089 ACCEPT_IF_APK
2090 }
2091
2092 class PerformFullRestoreTask implements Runnable {
2093 ParcelFileDescriptor mInputFile;
2094 IFullBackupRestoreObserver mObserver;
2095 AtomicBoolean mLatchObject;
2096 IBackupAgent mAgent;
2097 String mAgentPackage;
2098 ApplicationInfo mTargetApp;
2099 ParcelFileDescriptor[] mPipes = null;
2100
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002101 long mBytes;
2102
Christopher Tate75a99702011-05-18 16:28:19 -07002103 // possible handling states for a given package in the restore dataset
2104 final HashMap<String, RestorePolicy> mPackagePolicies
2105 = new HashMap<String, RestorePolicy>();
2106
2107 // installer package names for each encountered app, derived from the manifests
2108 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
2109
2110 // Signatures for a given package found in its manifest file
2111 final HashMap<String, Signature[]> mManifestSignatures
2112 = new HashMap<String, Signature[]>();
2113
2114 // Packages we've already wiped data on when restoring their first file
2115 final HashSet<String> mClearedPackages = new HashSet<String>();
2116
2117 PerformFullRestoreTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
2118 AtomicBoolean latch) {
2119 mInputFile = fd;
2120 mObserver = observer;
2121 mLatchObject = latch;
2122 mAgent = null;
2123 mAgentPackage = null;
2124 mTargetApp = null;
2125
2126 // Which packages we've already wiped data on. We prepopulate this
2127 // with a whitelist of packages known to be unclearable.
2128 mClearedPackages.add("android");
Christopher Tate75a99702011-05-18 16:28:19 -07002129 mClearedPackages.add("com.android.providers.settings");
Christopher Tateb0628bf2011-06-02 15:08:13 -07002130
Christopher Tate75a99702011-05-18 16:28:19 -07002131 }
2132
2133 class RestoreFileRunnable implements Runnable {
2134 IBackupAgent mAgent;
2135 FileMetadata mInfo;
2136 ParcelFileDescriptor mSocket;
2137 int mToken;
2138
2139 RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
2140 ParcelFileDescriptor socket, int token) throws IOException {
2141 mAgent = agent;
2142 mInfo = info;
2143 mToken = token;
2144
2145 // This class is used strictly for process-local binder invocations. The
2146 // semantics of ParcelFileDescriptor differ in this case; in particular, we
2147 // do not automatically get a 'dup'ed descriptor that we can can continue
2148 // to use asynchronously from the caller. So, we make sure to dup it ourselves
2149 // before proceeding to do the restore.
2150 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
2151 }
2152
2153 @Override
2154 public void run() {
2155 try {
2156 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
2157 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
2158 mToken, mBackupManagerBinder);
2159 } catch (RemoteException e) {
2160 // never happens; this is used strictly for local binder calls
2161 }
2162 }
2163 }
2164
2165 @Override
2166 public void run() {
2167 Slog.i(TAG, "--- Performing full-dataset restore ---");
2168 sendStartRestore();
2169
Christopher Tateb0628bf2011-06-02 15:08:13 -07002170 // Are we able to restore shared-storage data?
2171 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
2172 mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
2173 }
2174
Christopher Tate75a99702011-05-18 16:28:19 -07002175 try {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002176 mBytes = 0;
Christopher Tate75a99702011-05-18 16:28:19 -07002177 byte[] buffer = new byte[32 * 1024];
Christopher Tate7926a692011-07-11 11:31:57 -07002178 FileInputStream rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
2179 InflaterInputStream in = new InflaterInputStream(rawInStream);
Christopher Tate75a99702011-05-18 16:28:19 -07002180
2181 boolean didRestore;
2182 do {
Christopher Tate7926a692011-07-11 11:31:57 -07002183 didRestore = restoreOneFile(in, buffer);
Christopher Tate75a99702011-05-18 16:28:19 -07002184 } while (didRestore);
2185
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002186 if (DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
Christopher Tate75a99702011-05-18 16:28:19 -07002187 } finally {
2188 tearDownPipes();
2189 tearDownAgent(mTargetApp);
2190
2191 try {
2192 mInputFile.close();
2193 } catch (IOException e) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002194 Slog.w(TAG, "Close of restore data pipe threw", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002195 /* nothing we can do about this */
2196 }
2197 synchronized (mCurrentOpLock) {
2198 mCurrentOperations.clear();
2199 }
2200 synchronized (mLatchObject) {
2201 mLatchObject.set(true);
2202 mLatchObject.notifyAll();
2203 }
2204 sendEndRestore();
2205 mWakelock.release();
2206 if (DEBUG) Slog.d(TAG, "Full restore pass complete.");
2207 }
2208 }
2209
2210 boolean restoreOneFile(InputStream instream, byte[] buffer) {
2211 FileMetadata info;
2212 try {
2213 info = readTarHeaders(instream);
2214 if (info != null) {
2215 if (DEBUG) {
2216 dumpFileMetadata(info);
2217 }
2218
2219 final String pkg = info.packageName;
2220 if (!pkg.equals(mAgentPackage)) {
2221 // okay, change in package; set up our various
2222 // bookkeeping if we haven't seen it yet
2223 if (!mPackagePolicies.containsKey(pkg)) {
2224 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2225 }
2226
2227 // Clean up the previous agent relationship if necessary,
2228 // and let the observer know we're considering a new app.
2229 if (mAgent != null) {
2230 if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
2231 tearDownPipes();
2232 tearDownAgent(mTargetApp);
2233 mTargetApp = null;
2234 mAgentPackage = null;
2235 }
2236 }
2237
2238 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2239 mPackagePolicies.put(pkg, readAppManifest(info, instream));
2240 mPackageInstallers.put(pkg, info.installerPackageName);
2241 // We've read only the manifest content itself at this point,
2242 // so consume the footer before looping around to the next
2243 // input file
2244 skipTarPadding(info.size, instream);
2245 sendOnRestorePackage(pkg);
2246 } else {
2247 // Non-manifest, so it's actual file data. Is this a package
2248 // we're ignoring?
2249 boolean okay = true;
2250 RestorePolicy policy = mPackagePolicies.get(pkg);
2251 switch (policy) {
2252 case IGNORE:
2253 okay = false;
2254 break;
2255
2256 case ACCEPT_IF_APK:
2257 // If we're in accept-if-apk state, then the first file we
2258 // see MUST be the apk.
2259 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2260 if (DEBUG) Slog.d(TAG, "APK file; installing");
2261 // Try to install the app.
2262 String installerName = mPackageInstallers.get(pkg);
2263 okay = installApk(info, installerName, instream);
2264 // good to go; promote to ACCEPT
2265 mPackagePolicies.put(pkg, (okay)
2266 ? RestorePolicy.ACCEPT
2267 : RestorePolicy.IGNORE);
2268 // At this point we've consumed this file entry
2269 // ourselves, so just strip the tar footer and
2270 // go on to the next file in the input stream
2271 skipTarPadding(info.size, instream);
2272 return true;
2273 } else {
2274 // File data before (or without) the apk. We can't
2275 // handle it coherently in this case so ignore it.
2276 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2277 okay = false;
2278 }
2279 break;
2280
2281 case ACCEPT:
2282 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
2283 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
2284 // we can take the data without the apk, so we
2285 // *want* to do so. skip the apk by declaring this
2286 // one file not-okay without changing the restore
2287 // policy for the package.
2288 okay = false;
2289 }
2290 break;
2291
2292 default:
2293 // Something has gone dreadfully wrong when determining
2294 // the restore policy from the manifest. Ignore the
2295 // rest of this package's data.
2296 Slog.e(TAG, "Invalid policy from manifest");
2297 okay = false;
2298 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2299 break;
2300 }
2301
2302 // If the policy is satisfied, go ahead and set up to pipe the
2303 // data to the agent.
2304 if (DEBUG && okay && mAgent != null) {
2305 Slog.i(TAG, "Reusing existing agent instance");
2306 }
2307 if (okay && mAgent == null) {
2308 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
2309
2310 try {
2311 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
2312
2313 // If we haven't sent any data to this app yet, we probably
2314 // need to clear it first. Check that.
2315 if (!mClearedPackages.contains(pkg)) {
Christopher Tate79ec80d2011-06-24 14:58:49 -07002316 // apps with their own backup agents are
Christopher Tate75a99702011-05-18 16:28:19 -07002317 // responsible for coherently managing a full
2318 // restore.
Christopher Tate79ec80d2011-06-24 14:58:49 -07002319 if (mTargetApp.backupAgentName == null) {
Christopher Tate75a99702011-05-18 16:28:19 -07002320 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
2321 clearApplicationDataSynchronous(pkg);
2322 } else {
Christopher Tate79ec80d2011-06-24 14:58:49 -07002323 if (DEBUG) Slog.d(TAG, "backup agent ("
2324 + mTargetApp.backupAgentName + ") => no clear");
Christopher Tate75a99702011-05-18 16:28:19 -07002325 }
2326 mClearedPackages.add(pkg);
2327 } else {
2328 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
2329 }
2330
2331 // All set; now set up the IPC and launch the agent
2332 setUpPipes();
2333 mAgent = bindToAgentSynchronous(mTargetApp,
2334 IApplicationThread.BACKUP_MODE_RESTORE_FULL);
2335 mAgentPackage = pkg;
2336 } catch (IOException e) {
2337 // fall through to error handling
2338 } catch (NameNotFoundException e) {
2339 // fall through to error handling
2340 }
2341
2342 if (mAgent == null) {
2343 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
2344 okay = false;
2345 tearDownPipes();
2346 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2347 }
2348 }
2349
2350 // Sanity check: make sure we never give data to the wrong app. This
2351 // should never happen but a little paranoia here won't go amiss.
2352 if (okay && !pkg.equals(mAgentPackage)) {
2353 Slog.e(TAG, "Restoring data for " + pkg
2354 + " but agent is for " + mAgentPackage);
2355 okay = false;
2356 }
2357
2358 // At this point we have an agent ready to handle the full
2359 // restore data as well as a pipe for sending data to
2360 // that agent. Tell the agent to start reading from the
2361 // pipe.
2362 if (okay) {
2363 boolean agentSuccess = true;
2364 long toCopy = info.size;
2365 final int token = generateToken();
2366 try {
2367 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
2368 + info.path);
2369 prepareOperationTimeout(token,
2370 TIMEOUT_FULL_BACKUP_INTERVAL);
2371 // fire up the app's agent listening on the socket. If
2372 // the agent is running in the system process we can't
2373 // just invoke it asynchronously, so we provide a thread
2374 // for it here.
2375 if (mTargetApp.processName.equals("system")) {
2376 Slog.d(TAG, "system process agent - spinning a thread");
2377 RestoreFileRunnable runner = new RestoreFileRunnable(
2378 mAgent, info, mPipes[0], token);
2379 new Thread(runner).start();
2380 } else {
2381 mAgent.doRestoreFile(mPipes[0], info.size, info.type,
2382 info.domain, info.path, info.mode, info.mtime,
2383 token, mBackupManagerBinder);
2384 }
2385 } catch (IOException e) {
2386 // couldn't dup the socket for a process-local restore
2387 Slog.d(TAG, "Couldn't establish restore");
2388 agentSuccess = false;
2389 okay = false;
2390 } catch (RemoteException e) {
2391 // whoops, remote agent went away. We'll eat the content
2392 // ourselves, then, and not copy it over.
2393 Slog.e(TAG, "Agent crashed during full restore");
2394 agentSuccess = false;
2395 okay = false;
2396 }
2397
2398 // Copy over the data if the agent is still good
2399 if (okay) {
2400 boolean pipeOkay = true;
2401 FileOutputStream pipe = new FileOutputStream(
2402 mPipes[1].getFileDescriptor());
2403 if (DEBUG) Slog.d(TAG, "Piping data to agent");
2404 while (toCopy > 0) {
2405 int toRead = (toCopy > buffer.length)
2406 ? buffer.length : (int)toCopy;
2407 int nRead = instream.read(buffer, 0, toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002408 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002409 if (nRead <= 0) break;
2410 toCopy -= nRead;
2411
2412 // send it to the output pipe as long as things
2413 // are still good
2414 if (pipeOkay) {
2415 try {
2416 pipe.write(buffer, 0, nRead);
2417 } catch (IOException e) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002418 Slog.e(TAG, "Failed to write to restore pipe", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002419 pipeOkay = false;
2420 }
2421 }
2422 }
2423
2424 // done sending that file! Now we just need to consume
2425 // the delta from info.size to the end of block.
2426 skipTarPadding(info.size, instream);
2427
2428 // and now that we've sent it all, wait for the remote
2429 // side to acknowledge receipt
2430 agentSuccess = waitUntilOperationComplete(token);
2431 }
2432
2433 // okay, if the remote end failed at any point, deal with
2434 // it by ignoring the rest of the restore on it
2435 if (!agentSuccess) {
2436 mBackupHandler.removeMessages(MSG_TIMEOUT);
2437 tearDownPipes();
2438 tearDownAgent(mTargetApp);
2439 mAgent = null;
2440 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
2441 }
2442 }
2443
2444 // Problems setting up the agent communication, or an already-
2445 // ignored package: skip to the next tar stream entry by
2446 // reading and discarding this file.
2447 if (!okay) {
2448 if (DEBUG) Slog.d(TAG, "[discarding file content]");
2449 long bytesToConsume = (info.size + 511) & ~511;
2450 while (bytesToConsume > 0) {
2451 int toRead = (bytesToConsume > buffer.length)
2452 ? buffer.length : (int)bytesToConsume;
2453 long nRead = instream.read(buffer, 0, toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002454 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002455 if (nRead <= 0) break;
2456 bytesToConsume -= nRead;
2457 }
2458 }
2459 }
2460 }
2461 } catch (IOException e) {
2462 Slog.w(TAG, "io exception on restore socket read", e);
2463 // treat as EOF
2464 info = null;
2465 }
2466
2467 return (info != null);
2468 }
2469
2470 void setUpPipes() throws IOException {
2471 mPipes = ParcelFileDescriptor.createPipe();
2472 }
2473
2474 void tearDownPipes() {
2475 if (mPipes != null) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002476 try {
2477 mPipes[0].close();
2478 mPipes[0] = null;
2479 mPipes[1].close();
2480 mPipes[1] = null;
2481 } catch (IOException e) {
2482 Slog.w(TAG, "Couldn't close agent pipes", e);
Christopher Tate75a99702011-05-18 16:28:19 -07002483 }
2484 mPipes = null;
2485 }
2486 }
2487
2488 void tearDownAgent(ApplicationInfo app) {
2489 if (mAgent != null) {
2490 try {
2491 // unbind and tidy up even on timeout or failure, just in case
2492 mActivityManager.unbindBackupAgent(app);
2493
2494 // The agent was running with a stub Application object, so shut it down.
2495 // !!! We hardcode the confirmation UI's package name here rather than use a
2496 // manifest flag! TODO something less direct.
2497 if (app.uid != Process.SYSTEM_UID
2498 && !app.packageName.equals("com.android.backupconfirm")) {
2499 if (DEBUG) Slog.d(TAG, "Killing host process");
2500 mActivityManager.killApplicationProcess(app.processName, app.uid);
2501 } else {
2502 if (DEBUG) Slog.d(TAG, "Not killing after full restore");
2503 }
2504 } catch (RemoteException e) {
2505 Slog.d(TAG, "Lost app trying to shut down");
2506 }
2507 mAgent = null;
2508 }
2509 }
2510
2511 class RestoreInstallObserver extends IPackageInstallObserver.Stub {
2512 final AtomicBoolean mDone = new AtomicBoolean();
Christopher Tatea858cb02011-06-03 12:27:51 -07002513 String mPackageName;
Christopher Tate75a99702011-05-18 16:28:19 -07002514 int mResult;
2515
2516 public void reset() {
2517 synchronized (mDone) {
2518 mDone.set(false);
2519 }
2520 }
2521
2522 public void waitForCompletion() {
2523 synchronized (mDone) {
2524 while (mDone.get() == false) {
2525 try {
2526 mDone.wait();
2527 } catch (InterruptedException e) { }
2528 }
2529 }
2530 }
2531
2532 int getResult() {
2533 return mResult;
2534 }
2535
2536 @Override
2537 public void packageInstalled(String packageName, int returnCode)
2538 throws RemoteException {
2539 synchronized (mDone) {
2540 mResult = returnCode;
Christopher Tatea858cb02011-06-03 12:27:51 -07002541 mPackageName = packageName;
Christopher Tate75a99702011-05-18 16:28:19 -07002542 mDone.set(true);
2543 mDone.notifyAll();
2544 }
2545 }
2546 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002547
2548 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
2549 final AtomicBoolean mDone = new AtomicBoolean();
2550 int mResult;
2551
2552 public void reset() {
2553 synchronized (mDone) {
2554 mDone.set(false);
2555 }
2556 }
2557
2558 public void waitForCompletion() {
2559 synchronized (mDone) {
2560 while (mDone.get() == false) {
2561 try {
2562 mDone.wait();
2563 } catch (InterruptedException e) { }
2564 }
2565 }
2566 }
2567
2568 @Override
2569 public void packageDeleted(String packageName, int returnCode) throws RemoteException {
2570 synchronized (mDone) {
2571 mResult = returnCode;
2572 mDone.set(true);
2573 mDone.notifyAll();
2574 }
2575 }
2576 }
2577
Christopher Tate75a99702011-05-18 16:28:19 -07002578 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
Christopher Tatea858cb02011-06-03 12:27:51 -07002579 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
Christopher Tate75a99702011-05-18 16:28:19 -07002580
2581 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
2582 boolean okay = true;
2583
2584 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
2585
2586 // The file content is an .apk file. Copy it out to a staging location and
2587 // attempt to install it.
2588 File apkFile = new File(mDataDir, info.packageName);
2589 try {
2590 FileOutputStream apkStream = new FileOutputStream(apkFile);
2591 byte[] buffer = new byte[32 * 1024];
2592 long size = info.size;
2593 while (size > 0) {
2594 long toRead = (buffer.length < size) ? buffer.length : size;
2595 int didRead = instream.read(buffer, 0, (int)toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002596 if (didRead >= 0) mBytes += didRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002597 apkStream.write(buffer, 0, didRead);
2598 size -= didRead;
2599 }
2600 apkStream.close();
2601
2602 // make sure the installer can read it
2603 apkFile.setReadable(true, false);
2604
2605 // Now install it
2606 Uri packageUri = Uri.fromFile(apkFile);
2607 mInstallObserver.reset();
2608 mPackageManager.installPackage(packageUri, mInstallObserver,
2609 PackageManager.INSTALL_REPLACE_EXISTING, installerPackage);
2610 mInstallObserver.waitForCompletion();
2611
2612 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
2613 // The only time we continue to accept install of data even if the
2614 // apk install failed is if we had already determined that we could
2615 // accept the data regardless.
2616 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
2617 okay = false;
2618 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002619 } else {
2620 // Okay, the install succeeded. Make sure it was the right app.
2621 boolean uninstall = false;
2622 if (!mInstallObserver.mPackageName.equals(info.packageName)) {
2623 Slog.w(TAG, "Restore stream claimed to include apk for "
2624 + info.packageName + " but apk was really "
2625 + mInstallObserver.mPackageName);
2626 // delete the package we just put in place; it might be fraudulent
2627 okay = false;
2628 uninstall = true;
2629 } else {
2630 try {
2631 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
2632 PackageManager.GET_SIGNATURES);
2633 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
2634 Slog.w(TAG, "Restore stream contains apk of package "
2635 + info.packageName + " but it disallows backup/restore");
2636 okay = false;
2637 } else {
2638 // So far so good -- do the signatures match the manifest?
2639 Signature[] sigs = mManifestSignatures.get(info.packageName);
2640 if (!signaturesMatch(sigs, pkg)) {
2641 Slog.w(TAG, "Installed app " + info.packageName
2642 + " signatures do not match restore manifest");
2643 okay = false;
2644 uninstall = true;
2645 }
2646 }
2647 } catch (NameNotFoundException e) {
2648 Slog.w(TAG, "Install of package " + info.packageName
2649 + " succeeded but now not found");
2650 okay = false;
2651 }
2652 }
2653
2654 // If we're not okay at this point, we need to delete the package
2655 // that we just installed.
2656 if (uninstall) {
2657 mDeleteObserver.reset();
2658 mPackageManager.deletePackage(mInstallObserver.mPackageName,
2659 mDeleteObserver, 0);
2660 mDeleteObserver.waitForCompletion();
2661 }
Christopher Tate75a99702011-05-18 16:28:19 -07002662 }
2663 } catch (IOException e) {
2664 Slog.e(TAG, "Unable to transcribe restored apk for install");
2665 okay = false;
2666 } finally {
2667 apkFile.delete();
2668 }
2669
2670 return okay;
2671 }
2672
2673 // Given an actual file content size, consume the post-content padding mandated
2674 // by the tar format.
2675 void skipTarPadding(long size, InputStream instream) throws IOException {
2676 long partial = (size + 512) % 512;
2677 if (partial > 0) {
2678 byte[] buffer = new byte[512];
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002679 int nRead = instream.read(buffer, 0, 512 - (int)partial);
2680 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002681 }
2682 }
2683
2684 // Returns a policy constant; takes a buffer arg to reduce memory churn
2685 RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
2686 throws IOException {
2687 // Fail on suspiciously large manifest files
2688 if (info.size > 64 * 1024) {
2689 throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
2690 }
2691 byte[] buffer = new byte[(int) info.size];
2692 int nRead = 0;
2693 while (nRead < info.size) {
2694 nRead += instream.read(buffer, nRead, (int)info.size - nRead);
2695 }
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002696 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002697
2698 RestorePolicy policy = RestorePolicy.IGNORE;
2699 String[] str = new String[1];
2700 int offset = 0;
2701
2702 try {
2703 offset = extractLine(buffer, offset, str);
2704 int version = Integer.parseInt(str[0]);
2705 if (version == BACKUP_MANIFEST_VERSION) {
2706 offset = extractLine(buffer, offset, str);
2707 String manifestPackage = str[0];
2708 // TODO: handle <original-package>
2709 if (manifestPackage.equals(info.packageName)) {
2710 offset = extractLine(buffer, offset, str);
2711 version = Integer.parseInt(str[0]); // app version
2712 offset = extractLine(buffer, offset, str);
2713 int platformVersion = Integer.parseInt(str[0]);
2714 offset = extractLine(buffer, offset, str);
2715 info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
2716 offset = extractLine(buffer, offset, str);
2717 boolean hasApk = str[0].equals("1");
2718 offset = extractLine(buffer, offset, str);
2719 int numSigs = Integer.parseInt(str[0]);
Christopher Tate75a99702011-05-18 16:28:19 -07002720 if (numSigs > 0) {
Christopher Tatea858cb02011-06-03 12:27:51 -07002721 Signature[] sigs = new Signature[numSigs];
Christopher Tate75a99702011-05-18 16:28:19 -07002722 for (int i = 0; i < numSigs; i++) {
2723 offset = extractLine(buffer, offset, str);
2724 sigs[i] = new Signature(str[0]);
2725 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002726 mManifestSignatures.put(info.packageName, sigs);
Christopher Tate75a99702011-05-18 16:28:19 -07002727
2728 // Okay, got the manifest info we need...
2729 try {
Christopher Tate75a99702011-05-18 16:28:19 -07002730 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2731 info.packageName, PackageManager.GET_SIGNATURES);
Christopher Tatea858cb02011-06-03 12:27:51 -07002732 // Fall through to IGNORE if the app explicitly disallows backup
2733 final int flags = pkgInfo.applicationInfo.flags;
2734 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
2735 // Verify signatures against any installed version; if they
2736 // don't match, then we fall though and ignore the data. The
2737 // signatureMatch() method explicitly ignores the signature
2738 // check for packages installed on the system partition, because
2739 // such packages are signed with the platform cert instead of
2740 // the app developer's cert, so they're different on every
2741 // device.
2742 if (signaturesMatch(sigs, pkgInfo)) {
2743 if (pkgInfo.versionCode >= version) {
2744 Slog.i(TAG, "Sig + version match; taking data");
2745 policy = RestorePolicy.ACCEPT;
2746 } else {
2747 // The data is from a newer version of the app than
2748 // is presently installed. That means we can only
2749 // use it if the matching apk is also supplied.
2750 Slog.d(TAG, "Data version " + version
2751 + " is newer than installed version "
2752 + pkgInfo.versionCode + " - requiring apk");
2753 policy = RestorePolicy.ACCEPT_IF_APK;
2754 }
Christopher Tate75a99702011-05-18 16:28:19 -07002755 } else {
Christopher Tatea858cb02011-06-03 12:27:51 -07002756 Slog.w(TAG, "Restore manifest signatures do not match "
2757 + "installed application for " + info.packageName);
Christopher Tate75a99702011-05-18 16:28:19 -07002758 }
Christopher Tatea858cb02011-06-03 12:27:51 -07002759 } else {
2760 if (DEBUG) Slog.i(TAG, "Restore manifest from "
2761 + info.packageName + " but allowBackup=false");
Christopher Tate75a99702011-05-18 16:28:19 -07002762 }
2763 } catch (NameNotFoundException e) {
2764 // Okay, the target app isn't installed. We can process
2765 // the restore properly only if the dataset provides the
2766 // apk file and we can successfully install it.
2767 if (DEBUG) Slog.i(TAG, "Package " + info.packageName
2768 + " not installed; requiring apk in dataset");
2769 policy = RestorePolicy.ACCEPT_IF_APK;
2770 }
2771
2772 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
2773 Slog.i(TAG, "Cannot restore package " + info.packageName
2774 + " without the matching .apk");
2775 }
2776 } else {
2777 Slog.i(TAG, "Missing signature on backed-up package "
2778 + info.packageName);
2779 }
2780 } else {
2781 Slog.i(TAG, "Expected package " + info.packageName
2782 + " but restore manifest claims " + manifestPackage);
2783 }
2784 } else {
2785 Slog.i(TAG, "Unknown restore manifest version " + version
2786 + " for package " + info.packageName);
2787 }
2788 } catch (NumberFormatException e) {
2789 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
2790 }
2791
2792 return policy;
2793 }
2794
2795 // Builds a line from a byte buffer starting at 'offset', and returns
2796 // the index of the next unconsumed data in the buffer.
2797 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
2798 final int end = buffer.length;
2799 if (offset >= end) throw new IOException("Incomplete data");
2800
2801 int pos;
2802 for (pos = offset; pos < end; pos++) {
2803 byte c = buffer[pos];
2804 // at LF we declare end of line, and return the next char as the
2805 // starting point for the next time through
2806 if (c == '\n') {
2807 break;
2808 }
2809 }
2810 outStr[0] = new String(buffer, offset, pos - offset);
2811 pos++; // may be pointing an extra byte past the end but that's okay
2812 return pos;
2813 }
2814
2815 void dumpFileMetadata(FileMetadata info) {
2816 if (DEBUG) {
2817 StringBuilder b = new StringBuilder(128);
2818
2819 // mode string
Christopher Tate79ec80d2011-06-24 14:58:49 -07002820 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
Christopher Tate75a99702011-05-18 16:28:19 -07002821 b.append(((info.mode & 0400) != 0) ? 'r' : '-');
2822 b.append(((info.mode & 0200) != 0) ? 'w' : '-');
2823 b.append(((info.mode & 0100) != 0) ? 'x' : '-');
2824 b.append(((info.mode & 0040) != 0) ? 'r' : '-');
2825 b.append(((info.mode & 0020) != 0) ? 'w' : '-');
2826 b.append(((info.mode & 0010) != 0) ? 'x' : '-');
2827 b.append(((info.mode & 0004) != 0) ? 'r' : '-');
2828 b.append(((info.mode & 0002) != 0) ? 'w' : '-');
2829 b.append(((info.mode & 0001) != 0) ? 'x' : '-');
2830 b.append(String.format(" %9d ", info.size));
2831
2832 Date stamp = new Date(info.mtime);
2833 b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
2834
2835 b.append(info.packageName);
2836 b.append(" :: ");
2837 b.append(info.domain);
2838 b.append(" :: ");
2839 b.append(info.path);
2840
2841 Slog.i(TAG, b.toString());
2842 }
2843 }
2844 // Consume a tar file header block [sequence] and accumulate the relevant metadata
2845 FileMetadata readTarHeaders(InputStream instream) throws IOException {
2846 byte[] block = new byte[512];
2847 FileMetadata info = null;
2848
2849 boolean gotHeader = readTarHeader(instream, block);
2850 if (gotHeader) {
2851 // okay, presume we're okay, and extract the various metadata
2852 info = new FileMetadata();
2853 info.size = extractRadix(block, 124, 12, 8);
2854 info.mtime = extractRadix(block, 136, 12, 8);
2855 info.mode = extractRadix(block, 100, 8, 8);
2856
2857 info.path = extractString(block, 345, 155); // prefix
2858 String path = extractString(block, 0, 100);
2859 if (path.length() > 0) {
2860 if (info.path.length() > 0) info.path += '/';
2861 info.path += path;
2862 }
2863
2864 // tar link indicator field: 1 byte at offset 156 in the header.
2865 int typeChar = block[156];
2866 if (typeChar == 'x') {
2867 // pax extended header, so we need to read that
2868 gotHeader = readPaxExtendedHeader(instream, info);
2869 if (gotHeader) {
2870 // and after a pax extended header comes another real header -- read
2871 // that to find the real file type
2872 gotHeader = readTarHeader(instream, block);
2873 }
2874 if (!gotHeader) throw new IOException("Bad or missing pax header");
2875
2876 typeChar = block[156];
2877 }
2878
2879 switch (typeChar) {
Christopher Tate79ec80d2011-06-24 14:58:49 -07002880 case '0': info.type = BackupAgent.TYPE_FILE; break;
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002881 case '5': {
Christopher Tate79ec80d2011-06-24 14:58:49 -07002882 info.type = BackupAgent.TYPE_DIRECTORY;
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002883 if (info.size != 0) {
2884 Slog.w(TAG, "Directory entry with nonzero size in header");
2885 info.size = 0;
2886 }
2887 break;
2888 }
Christopher Tate75a99702011-05-18 16:28:19 -07002889 case 0: {
2890 // presume EOF
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002891 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
Christopher Tate75a99702011-05-18 16:28:19 -07002892 return null;
2893 }
2894 default: {
2895 Slog.e(TAG, "Unknown tar entity type: " + typeChar);
2896 throw new IOException("Unknown entity type " + typeChar);
2897 }
2898 }
2899
2900 // Parse out the path
2901 //
2902 // first: apps/shared/unrecognized
2903 if (FullBackup.SHARED_PREFIX.regionMatches(0,
2904 info.path, 0, FullBackup.SHARED_PREFIX.length())) {
2905 // File in shared storage. !!! TODO: implement this.
2906 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
Christopher Tateb0628bf2011-06-02 15:08:13 -07002907 info.packageName = "com.android.sharedstoragebackup";
Christopher Tate75a99702011-05-18 16:28:19 -07002908 info.domain = FullBackup.SHARED_STORAGE_TOKEN;
Christopher Tateb0628bf2011-06-02 15:08:13 -07002909 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
Christopher Tate75a99702011-05-18 16:28:19 -07002910 } else if (FullBackup.APPS_PREFIX.regionMatches(0,
2911 info.path, 0, FullBackup.APPS_PREFIX.length())) {
2912 // App content! Parse out the package name and domain
2913
2914 // strip the apps/ prefix
2915 info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
2916
2917 // extract the package name
2918 int slash = info.path.indexOf('/');
2919 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
2920 info.packageName = info.path.substring(0, slash);
2921 info.path = info.path.substring(slash+1);
2922
2923 // if it's a manifest we're done, otherwise parse out the domains
2924 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
2925 slash = info.path.indexOf('/');
2926 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
2927 info.domain = info.path.substring(0, slash);
2928 // validate that it's one of the domains we understand
2929 if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
2930 && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
2931 && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
2932 && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
2933 && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
2934 && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
2935 && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
2936 throw new IOException("Unrecognized domain " + info.domain);
2937 }
2938
2939 info.path = info.path.substring(slash + 1);
2940 }
2941 }
2942 }
2943 return info;
2944 }
2945
2946 boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
2947 int nRead = instream.read(block, 0, 512);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002948 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002949 if (nRead > 0 && nRead != 512) {
2950 // if we read only a partial block, then things are
2951 // clearly screwed up. terminate the restore.
2952 throw new IOException("Partial header block: " + nRead);
2953 }
2954 return (nRead > 0);
2955 }
2956
2957 // overwrites 'info' fields based on the pax extended header
2958 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
2959 throws IOException {
2960 // We should never see a pax extended header larger than this
2961 if (info.size > 32*1024) {
2962 Slog.w(TAG, "Suspiciously large pax header size " + info.size
2963 + " - aborting");
2964 throw new IOException("Sanity failure: pax header size " + info.size);
2965 }
2966
2967 // read whole blocks, not just the content size
2968 int numBlocks = (int)((info.size + 511) >> 9);
2969 byte[] data = new byte[numBlocks * 512];
2970 int nRead = instream.read(data);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002971 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07002972 if (nRead != data.length) {
2973 return false;
2974 }
2975
2976 final int contentSize = (int) info.size;
2977 int offset = 0;
2978 do {
2979 // extract the line at 'offset'
2980 int eol = offset+1;
2981 while (eol < contentSize && data[eol] != ' ') eol++;
2982 if (eol >= contentSize) {
2983 // error: we just hit EOD looking for the end of the size field
2984 throw new IOException("Invalid pax data");
2985 }
2986 // eol points to the space between the count and the key
2987 int linelen = (int) extractRadix(data, offset, eol - offset, 10);
2988 int key = eol + 1; // start of key=value
2989 eol = offset + linelen - 1; // trailing LF
2990 int value;
2991 for (value = key+1; data[value] != '=' && value <= eol; value++);
2992 if (value > eol) {
2993 throw new IOException("Invalid pax declaration");
2994 }
2995
2996 // pax requires that key/value strings be in UTF-8
2997 String keyStr = new String(data, key, value-key, "UTF-8");
2998 // -1 to strip the trailing LF
2999 String valStr = new String(data, value+1, eol-value-1, "UTF-8");
3000
3001 if ("path".equals(keyStr)) {
3002 info.path = valStr;
3003 } else if ("size".equals(keyStr)) {
3004 info.size = Long.parseLong(valStr);
3005 } else {
3006 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
3007 }
3008
3009 offset += linelen;
3010 } while (offset < contentSize);
3011
3012 return true;
3013 }
3014
3015 long extractRadix(byte[] data, int offset, int maxChars, int radix)
3016 throws IOException {
3017 long value = 0;
3018 final int end = offset + maxChars;
3019 for (int i = offset; i < end; i++) {
3020 final byte b = data[i];
Christopher Tate3f6c77b2011-06-07 13:17:17 -07003021 // Numeric fields in tar can terminate with either NUL or SPC
Christopher Tate75a99702011-05-18 16:28:19 -07003022 if (b == 0 || b == ' ') break;
3023 if (b < '0' || b > ('0' + radix - 1)) {
3024 throw new IOException("Invalid number in header");
3025 }
3026 value = radix * value + (b - '0');
3027 }
3028 return value;
3029 }
3030
3031 String extractString(byte[] data, int offset, int maxChars) throws IOException {
3032 final int end = offset + maxChars;
3033 int eos = offset;
Christopher Tate3f6c77b2011-06-07 13:17:17 -07003034 // tar string fields terminate early with a NUL
3035 while (eos < end && data[eos] != 0) eos++;
Christopher Tate75a99702011-05-18 16:28:19 -07003036 return new String(data, offset, eos-offset, "US-ASCII");
3037 }
3038
3039 void sendStartRestore() {
3040 if (mObserver != null) {
3041 try {
3042 mObserver.onStartRestore();
3043 } catch (RemoteException e) {
3044 Slog.w(TAG, "full restore observer went away: startRestore");
3045 mObserver = null;
3046 }
3047 }
3048 }
3049
3050 void sendOnRestorePackage(String name) {
3051 if (mObserver != null) {
3052 try {
3053 // TODO: use a more user-friendly name string
3054 mObserver.onRestorePackage(name);
3055 } catch (RemoteException e) {
3056 Slog.w(TAG, "full restore observer went away: restorePackage");
3057 mObserver = null;
3058 }
3059 }
3060 }
3061
3062 void sendEndRestore() {
3063 if (mObserver != null) {
3064 try {
3065 mObserver.onEndRestore();
3066 } catch (RemoteException e) {
3067 Slog.w(TAG, "full restore observer went away: endRestore");
3068 mObserver = null;
3069 }
3070 }
3071 }
3072 }
3073
Christopher Tatedf01dea2009-06-09 20:45:02 -07003074 // ----- Restore handling -----
3075
Christopher Tate78dd4a72009-11-04 11:49:08 -08003076 private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
3077 // If the target resides on the system partition, we allow it to restore
3078 // data from the like-named package in a restore set even if the signatures
3079 // do not match. (Unlike general applications, those flashed to the system
3080 // partition will be signed with the device's platform certificate, so on
3081 // different phones the same system app will have different signatures.)
3082 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003083 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
Christopher Tate78dd4a72009-11-04 11:49:08 -08003084 return true;
3085 }
3086
Christopher Tate20efdf6b2009-06-18 19:41:36 -07003087 // Allow unsigned apps, but not signed on one device and unsigned on the other
3088 // !!! TODO: is this the right policy?
Christopher Tate78dd4a72009-11-04 11:49:08 -08003089 Signature[] deviceSigs = target.signatures;
Joe Onorato8a9b2202010-02-26 18:56:32 -08003090 if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
Christopher Tate6aa41f42009-06-19 14:14:22 -07003091 + " device=" + deviceSigs);
Christopher Tate20efdf6b2009-06-18 19:41:36 -07003092 if ((storedSigs == null || storedSigs.length == 0)
3093 && (deviceSigs == null || deviceSigs.length == 0)) {
3094 return true;
3095 }
3096 if (storedSigs == null || deviceSigs == null) {
3097 return false;
3098 }
3099
Christopher Tateabce4e82009-06-18 18:35:32 -07003100 // !!! TODO: this demands that every stored signature match one
3101 // that is present on device, and does not demand the converse.
3102 // Is this this right policy?
3103 int nStored = storedSigs.length;
3104 int nDevice = deviceSigs.length;
3105
3106 for (int i=0; i < nStored; i++) {
3107 boolean match = false;
3108 for (int j=0; j < nDevice; j++) {
3109 if (storedSigs[i].equals(deviceSigs[j])) {
3110 match = true;
3111 break;
3112 }
3113 }
3114 if (!match) {
3115 return false;
3116 }
3117 }
3118 return true;
3119 }
3120
Christopher Tate44a27902010-01-27 17:15:49 -08003121 class PerformRestoreTask implements Runnable {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003122 private IBackupTransport mTransport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003123 private IRestoreObserver mObserver;
Dan Egnor156411d2009-06-26 13:20:02 -07003124 private long mToken;
Christopher Tate84725812010-02-04 15:52:40 -08003125 private PackageInfo mTargetPackage;
Christopher Tate5cb400b2009-06-25 16:03:14 -07003126 private File mStateDir;
Christopher Tate1bb69062010-02-19 17:02:12 -08003127 private int mPmToken;
Chris Tate249345b2010-10-29 12:57:04 -07003128 private boolean mNeedFullBackup;
Christopher Tate284f1bb2011-07-07 14:31:18 -07003129 private HashSet<String> mFilterSet;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003130
Christopher Tate5cbbf562009-06-22 16:44:51 -07003131 class RestoreRequest {
3132 public PackageInfo app;
3133 public int storedAppVersion;
3134
3135 RestoreRequest(PackageInfo _app, int _version) {
3136 app = _app;
3137 storedAppVersion = _version;
3138 }
3139 }
3140
Christopher Tate44a27902010-01-27 17:15:49 -08003141 PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
Chris Tate249345b2010-10-29 12:57:04 -07003142 long restoreSetToken, PackageInfo targetPackage, int pmToken,
Christopher Tate284f1bb2011-07-07 14:31:18 -07003143 boolean needFullBackup, String[] filterSet) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003144 mTransport = transport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003145 mObserver = observer;
Christopher Tate9bbc21a2009-06-10 20:23:25 -07003146 mToken = restoreSetToken;
Christopher Tate84725812010-02-04 15:52:40 -08003147 mTargetPackage = targetPackage;
Christopher Tate1bb69062010-02-19 17:02:12 -08003148 mPmToken = pmToken;
Chris Tate249345b2010-10-29 12:57:04 -07003149 mNeedFullBackup = needFullBackup;
Christopher Tate5cb400b2009-06-25 16:03:14 -07003150
Christopher Tate284f1bb2011-07-07 14:31:18 -07003151 if (filterSet != null) {
3152 mFilterSet = new HashSet<String>();
3153 for (String pkg : filterSet) {
3154 mFilterSet.add(pkg);
3155 }
3156 } else {
3157 mFilterSet = null;
3158 }
3159
Christopher Tate5cb400b2009-06-25 16:03:14 -07003160 try {
3161 mStateDir = new File(mBaseStateDir, transport.transportDirName());
3162 } catch (RemoteException e) {
3163 // can't happen; the transport is local
3164 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003165 }
3166
Christopher Tatedf01dea2009-06-09 20:45:02 -07003167 public void run() {
Dan Egnorbb9001c2009-07-27 12:20:13 -07003168 long startRealtime = SystemClock.elapsedRealtime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003169 if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
Christopher Tate84725812010-02-04 15:52:40 -08003170 + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
Christopher Tate284f1bb2011-07-07 14:31:18 -07003171 + " mTargetPackage=" + mTargetPackage + " mFilterSet=" + mFilterSet
3172 + " mPmToken=" + mPmToken);
Christopher Tatedf01dea2009-06-09 20:45:02 -07003173
Christopher Tateb49ceb32010-02-08 16:22:24 -08003174 PackageManagerBackupAgent pmAgent = null;
Christopher Tate7d562ec2009-06-25 18:03:43 -07003175 int error = -1; // assume error
3176
Dan Egnorefe52642009-06-24 00:16:33 -07003177 // build the set of apps to restore
Christopher Tatedf01dea2009-06-09 20:45:02 -07003178 try {
Dan Egnorbb9001c2009-07-27 12:20:13 -07003179 // TODO: Log this before getAvailableRestoreSets, somehow
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003180 EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
Christopher Tateabce4e82009-06-18 18:35:32 -07003181
Dan Egnorefe52642009-06-24 00:16:33 -07003182 // Get the list of all packages which have backup enabled.
3183 // (Include the Package Manager metadata pseudo-package first.)
3184 ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
3185 PackageInfo omPackage = new PackageInfo();
3186 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
3187 restorePackages.add(omPackage);
Christopher Tatedf01dea2009-06-09 20:45:02 -07003188
Dan Egnorefe52642009-06-24 00:16:33 -07003189 List<PackageInfo> agentPackages = allAgentPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003190 if (mTargetPackage == null) {
Christopher Tate284f1bb2011-07-07 14:31:18 -07003191 // if there's a filter set, strip out anything that isn't
3192 // present before proceeding
3193 if (mFilterSet != null) {
3194 for (int i = agentPackages.size() - 1; i >= 0; i--) {
3195 final PackageInfo pkg = agentPackages.get(i);
3196 if (! mFilterSet.contains(pkg.packageName)) {
3197 agentPackages.remove(i);
3198 }
3199 }
3200 if (DEBUG) {
3201 Slog.i(TAG, "Post-filter package set for restore:");
3202 for (PackageInfo p : agentPackages) {
3203 Slog.i(TAG, " " + p);
3204 }
3205 }
3206 }
Christopher Tate84725812010-02-04 15:52:40 -08003207 restorePackages.addAll(agentPackages);
3208 } else {
3209 // Just one package to attempt restore of
3210 restorePackages.add(mTargetPackage);
3211 }
Dan Egnorefe52642009-06-24 00:16:33 -07003212
Christopher Tate7d562ec2009-06-25 18:03:43 -07003213 // let the observer know that we're running
3214 if (mObserver != null) {
3215 try {
3216 // !!! TODO: get an actual count from the transport after
3217 // its startRestore() runs?
3218 mObserver.restoreStarting(restorePackages.size());
3219 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003220 Slog.d(TAG, "Restore observer died at restoreStarting");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003221 mObserver = null;
3222 }
3223 }
3224
Dan Egnor01445162009-09-21 17:04:05 -07003225 if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
3226 BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003227 Slog.e(TAG, "Error starting restore operation");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003228 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003229 return;
3230 }
3231
3232 String packageName = mTransport.nextRestorePackage();
3233 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003234 Slog.e(TAG, "Error getting first restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003235 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003236 return;
3237 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003238 Slog.i(TAG, "No restore data available");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003239 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003240 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
Dan Egnorefe52642009-06-24 00:16:33 -07003241 return;
3242 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003243 Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
Dan Egnorefe52642009-06-24 00:16:33 -07003244 + "\", found only \"" + packageName + "\"");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003245 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003246 "Package manager data missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003247 return;
3248 }
3249
3250 // Pull the Package Manager metadata from the restore set first
Christopher Tateb49ceb32010-02-08 16:22:24 -08003251 pmAgent = new PackageManagerBackupAgent(
Dan Egnorefe52642009-06-24 00:16:33 -07003252 mPackageManager, agentPackages);
Chris Tate249345b2010-10-29 12:57:04 -07003253 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()),
3254 mNeedFullBackup);
Dan Egnorefe52642009-06-24 00:16:33 -07003255
Christopher Tate8c032472009-07-02 14:28:47 -07003256 // Verify that the backup set includes metadata. If not, we can't do
3257 // signature/version verification etc, so we simply do not proceed with
3258 // the restore operation.
Christopher Tate3d7cd132009-07-07 14:23:07 -07003259 if (!pmAgent.hasMetadata()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003260 Slog.e(TAG, "No restore metadata available, so not restoring settings");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003261 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003262 "Package manager restore metadata missing");
Christopher Tate8c032472009-07-02 14:28:47 -07003263 return;
3264 }
3265
Christopher Tate7d562ec2009-06-25 18:03:43 -07003266 int count = 0;
Dan Egnorefe52642009-06-24 00:16:33 -07003267 for (;;) {
3268 packageName = mTransport.nextRestorePackage();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003269
Dan Egnorefe52642009-06-24 00:16:33 -07003270 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003271 Slog.e(TAG, "Error getting next restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003272 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorefe52642009-06-24 00:16:33 -07003273 return;
3274 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003275 if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
Dan Egnorefe52642009-06-24 00:16:33 -07003276 break;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003277 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003278
Christopher Tate7d562ec2009-06-25 18:03:43 -07003279 if (mObserver != null) {
Christopher Tate7d562ec2009-06-25 18:03:43 -07003280 try {
Christopher Tate9c3cee92010-03-25 16:06:43 -07003281 mObserver.onUpdate(count, packageName);
Christopher Tate7d562ec2009-06-25 18:03:43 -07003282 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003283 Slog.d(TAG, "Restore observer died in onUpdate");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003284 mObserver = null;
3285 }
3286 }
3287
Dan Egnorefe52642009-06-24 00:16:33 -07003288 Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
3289 if (metaInfo == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003290 Slog.e(TAG, "Missing metadata for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003291 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003292 "Package metadata missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003293 continue;
3294 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003295
Dan Egnorbb9001c2009-07-27 12:20:13 -07003296 PackageInfo packageInfo;
3297 try {
3298 int flags = PackageManager.GET_SIGNATURES;
3299 packageInfo = mPackageManager.getPackageInfo(packageName, flags);
3300 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003301 Slog.e(TAG, "Invalid package restoring data", e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003302 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003303 "Package missing on device");
3304 continue;
3305 }
3306
Dan Egnorefe52642009-06-24 00:16:33 -07003307 if (metaInfo.versionCode > packageInfo.versionCode) {
Christopher Tate3dda5182010-02-24 16:06:18 -08003308 // Data is from a "newer" version of the app than we have currently
3309 // installed. If the app has not declared that it is prepared to
3310 // handle this case, we do not attempt the restore.
3311 if ((packageInfo.applicationInfo.flags
3312 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
3313 String message = "Version " + metaInfo.versionCode
3314 + " > installed version " + packageInfo.versionCode;
Joe Onorato8a9b2202010-02-26 18:56:32 -08003315 Slog.w(TAG, "Package " + packageName + ": " + message);
Christopher Tate3dda5182010-02-24 16:06:18 -08003316 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
3317 packageName, message);
3318 continue;
3319 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003320 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
Christopher Tate3dda5182010-02-24 16:06:18 -08003321 + " > installed " + packageInfo.versionCode
3322 + " but restoreAnyVersion");
3323 }
Dan Egnorefe52642009-06-24 00:16:33 -07003324 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003325
Christopher Tate78dd4a72009-11-04 11:49:08 -08003326 if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003327 Slog.w(TAG, "Signature mismatch restoring " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003328 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003329 "Signature mismatch");
Dan Egnorefe52642009-06-24 00:16:33 -07003330 continue;
3331 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003332
Joe Onorato8a9b2202010-02-26 18:56:32 -08003333 if (DEBUG) Slog.v(TAG, "Package " + packageName
Dan Egnorefe52642009-06-24 00:16:33 -07003334 + " restore version [" + metaInfo.versionCode
3335 + "] is compatible with installed version ["
3336 + packageInfo.versionCode + "]");
Christopher Tatec7b31e32009-06-10 15:49:30 -07003337
Christopher Tate3de55bc2010-03-12 17:28:08 -08003338 // Then set up and bind the agent
Dan Egnorefe52642009-06-24 00:16:33 -07003339 IBackupAgent agent = bindToAgentSynchronous(
3340 packageInfo.applicationInfo,
Christopher Tate3de55bc2010-03-12 17:28:08 -08003341 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Dan Egnorefe52642009-06-24 00:16:33 -07003342 if (agent == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003343 Slog.w(TAG, "Can't find backup agent for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003344 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07003345 "Restore agent missing");
Dan Egnorefe52642009-06-24 00:16:33 -07003346 continue;
Christopher Tatedf01dea2009-06-09 20:45:02 -07003347 }
3348
Christopher Tate5e1ab332009-09-01 20:32:49 -07003349 // And then finally run the restore on this agent
Dan Egnorefe52642009-06-24 00:16:33 -07003350 try {
Chris Tate249345b2010-10-29 12:57:04 -07003351 processOneRestore(packageInfo, metaInfo.versionCode, agent,
3352 mNeedFullBackup);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003353 ++count;
Dan Egnorefe52642009-06-24 00:16:33 -07003354 } finally {
Christopher Tate5e1ab332009-09-01 20:32:49 -07003355 // unbind and tidy up even on timeout or failure, just in case
Dan Egnorefe52642009-06-24 00:16:33 -07003356 mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
Christopher Tate5e1ab332009-09-01 20:32:49 -07003357
3358 // The agent was probably running with a stub Application object,
3359 // which isn't a valid run mode for the main app logic. Shut
3360 // down the app so that next time it's launched, it gets the
Christopher Tate3dda5182010-02-24 16:06:18 -08003361 // usual full initialization. Note that this is only done for
3362 // full-system restores: when a single app has requested a restore,
3363 // it is explicitly not killed following that operation.
3364 if (mTargetPackage == null && (packageInfo.applicationInfo.flags
Christopher Tate5e1ab332009-09-01 20:32:49 -07003365 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003366 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
Christopher Tate5e1ab332009-09-01 20:32:49 -07003367 + packageInfo.applicationInfo.processName);
3368 mActivityManager.killApplicationProcess(
3369 packageInfo.applicationInfo.processName,
3370 packageInfo.applicationInfo.uid);
3371 }
Dan Egnorefe52642009-06-24 00:16:33 -07003372 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003373 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003374
3375 // if we get this far, report success to the observer
3376 error = 0;
Dan Egnorbb9001c2009-07-27 12:20:13 -07003377 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003378 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, count, millis);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003379 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003380 Slog.e(TAG, "Error in restore thread", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003381 } finally {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003382 if (DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003383
Dan Egnorefe52642009-06-24 00:16:33 -07003384 try {
3385 mTransport.finishRestore();
3386 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003387 Slog.e(TAG, "Error finishing restore", e);
Dan Egnorefe52642009-06-24 00:16:33 -07003388 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07003389
3390 if (mObserver != null) {
3391 try {
3392 mObserver.restoreFinished(error);
3393 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003394 Slog.d(TAG, "Restore observer died at restoreFinished");
Christopher Tate7d562ec2009-06-25 18:03:43 -07003395 }
3396 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003397
Christopher Tate84725812010-02-04 15:52:40 -08003398 // If this was a restoreAll operation, record that this was our
Christopher Tateb49ceb32010-02-08 16:22:24 -08003399 // ancestral dataset, as well as the set of apps that are possibly
3400 // restoreable from the dataset
3401 if (mTargetPackage == null && pmAgent != null) {
3402 mAncestralPackages = pmAgent.getRestoredPackages();
Christopher Tate84725812010-02-04 15:52:40 -08003403 mAncestralToken = mToken;
3404 writeRestoreTokens();
3405 }
3406
Christopher Tate1bb69062010-02-19 17:02:12 -08003407 // We must under all circumstances tell the Package Manager to
3408 // proceed with install notifications if it's waiting for us.
3409 if (mPmToken > 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003410 if (DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
Christopher Tate1bb69062010-02-19 17:02:12 -08003411 try {
3412 mPackageManagerBinder.finishPackageInstall(mPmToken);
3413 } catch (RemoteException e) { /* can't happen */ }
3414 }
3415
Christopher Tate73a3cb32010-12-13 18:27:26 -08003416 // Furthermore we need to reset the session timeout clock
3417 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
3418 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
3419 TIMEOUT_RESTORE_INTERVAL);
3420
Christopher Tateb6787f22009-07-02 17:40:45 -07003421 // done; we can finally release the wakelock
3422 mWakelock.release();
Christopher Tatedf01dea2009-06-09 20:45:02 -07003423 }
3424 }
3425
Dan Egnorefe52642009-06-24 00:16:33 -07003426 // Do the guts of a restore of one application, using mTransport.getRestoreData().
Chris Tate249345b2010-10-29 12:57:04 -07003427 void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
3428 boolean needFullBackup) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07003429 // !!! TODO: actually run the restore through mTransport
Christopher Tatec7b31e32009-06-10 15:49:30 -07003430 final String packageName = app.packageName;
3431
Joe Onorato8a9b2202010-02-26 18:56:32 -08003432 if (DEBUG) Slog.d(TAG, "processOneRestore packageName=" + packageName);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04003433
Christopher Tatec7b31e32009-06-10 15:49:30 -07003434 // !!! TODO: get the dirs from the transport
3435 File backupDataName = new File(mDataDir, packageName + ".restore");
Dan Egnorbb9001c2009-07-27 12:20:13 -07003436 File newStateName = new File(mStateDir, packageName + ".new");
3437 File savedStateName = new File(mStateDir, packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003438
Dan Egnorbb9001c2009-07-27 12:20:13 -07003439 ParcelFileDescriptor backupData = null;
3440 ParcelFileDescriptor newState = null;
3441
Christopher Tate4a627c72011-04-01 14:43:32 -07003442 final int token = generateToken();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003443 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07003444 // Run the transport's restore pass
Dan Egnorbb9001c2009-07-27 12:20:13 -07003445 backupData = ParcelFileDescriptor.open(backupDataName,
3446 ParcelFileDescriptor.MODE_READ_WRITE |
3447 ParcelFileDescriptor.MODE_CREATE |
3448 ParcelFileDescriptor.MODE_TRUNCATE);
3449
Dan Egnor01445162009-09-21 17:04:05 -07003450 if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003451 Slog.e(TAG, "Error getting restore data for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003452 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Dan Egnorbb9001c2009-07-27 12:20:13 -07003453 return;
Christopher Tatec7b31e32009-06-10 15:49:30 -07003454 }
3455
3456 // Okay, we have the data. Now have the agent do the restore.
Dan Egnorbb9001c2009-07-27 12:20:13 -07003457 backupData.close();
Christopher Tatec7b31e32009-06-10 15:49:30 -07003458 backupData = ParcelFileDescriptor.open(backupDataName,
3459 ParcelFileDescriptor.MODE_READ_ONLY);
3460
Dan Egnorbb9001c2009-07-27 12:20:13 -07003461 newState = ParcelFileDescriptor.open(newStateName,
3462 ParcelFileDescriptor.MODE_READ_WRITE |
3463 ParcelFileDescriptor.MODE_CREATE |
3464 ParcelFileDescriptor.MODE_TRUNCATE);
3465
Christopher Tate44a27902010-01-27 17:15:49 -08003466 // Kick off the restore, checking for hung agents
3467 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL);
3468 agent.doRestore(backupData, appVersionCode, newState, token, mBackupManagerBinder);
3469 boolean success = waitUntilOperationComplete(token);
3470
3471 if (!success) {
3472 throw new RuntimeException("restore timeout");
3473 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003474
3475 // if everything went okay, remember the recorded state now
Christopher Tate90967f42009-09-20 15:28:33 -07003476 //
3477 // !!! TODO: the restored data should be migrated on the server
3478 // side into the current dataset. In that case the new state file
3479 // we just created would reflect the data already extant in the
3480 // backend, so there'd be nothing more to do. Until that happens,
3481 // however, we need to make sure that we record the data to the
3482 // current backend dataset. (Yes, this means shipping the data over
3483 // the wire in both directions. That's bad, but consistency comes
3484 // first, then efficiency.) Once we introduce server-side data
3485 // migration to the newly-restored device's dataset, we will change
3486 // the following from a discard of the newly-written state to the
3487 // "correct" operation of renaming into the canonical state blob.
3488 newStateName.delete(); // TODO: remove; see above comment
3489 //newStateName.renameTo(savedStateName); // TODO: replace with this
3490
Dan Egnorbb9001c2009-07-27 12:20:13 -07003491 int size = (int) backupDataName.length();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003492 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, packageName, size);
Christopher Tatec7b31e32009-06-10 15:49:30 -07003493 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003494 Slog.e(TAG, "Error restoring data for " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003495 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
Dan Egnorbb9001c2009-07-27 12:20:13 -07003496
Christopher Tate96733042009-07-20 14:49:13 -07003497 // If the agent fails restore, it might have put the app's data
3498 // into an incoherent state. For consistency we wipe its data
3499 // again in this case before propagating the exception
Christopher Tate96733042009-07-20 14:49:13 -07003500 clearApplicationDataSynchronous(packageName);
Christopher Tate1531dc82009-07-24 16:37:43 -07003501 } finally {
3502 backupDataName.delete();
Dan Egnorbb9001c2009-07-27 12:20:13 -07003503 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
3504 try { if (newState != null) newState.close(); } catch (IOException e) {}
3505 backupData = newState = null;
Christopher Tate4a627c72011-04-01 14:43:32 -07003506 synchronized (mCurrentOperations) {
3507 mCurrentOperations.delete(token);
3508 }
Chris Tate249345b2010-10-29 12:57:04 -07003509
3510 // If we know a priori that we'll need to perform a full post-restore backup
3511 // pass, clear the new state file data. This means we're discarding work that
3512 // was just done by the app's agent, but this way the agent doesn't need to
3513 // take any special action based on global device state.
3514 if (needFullBackup) {
3515 newStateName.delete();
3516 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07003517 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003518 }
3519 }
3520
Christopher Tate44a27902010-01-27 17:15:49 -08003521 class PerformClearTask implements Runnable {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003522 IBackupTransport mTransport;
3523 PackageInfo mPackage;
3524
Christopher Tate44a27902010-01-27 17:15:49 -08003525 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
Christopher Tateee0e78a2009-07-02 11:17:03 -07003526 mTransport = transport;
3527 mPackage = packageInfo;
3528 }
3529
Christopher Tateee0e78a2009-07-02 11:17:03 -07003530 public void run() {
3531 try {
3532 // Clear the on-device backup state to ensure a full backup next time
3533 File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
3534 File stateFile = new File(stateDir, mPackage.packageName);
3535 stateFile.delete();
3536
3537 // Tell the transport to remove all the persistent storage for the app
Christopher Tate13f4a642009-09-30 20:06:45 -07003538 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003539 mTransport.clearBackupData(mPackage);
3540 } catch (RemoteException e) {
3541 // can't happen; the transport is local
3542 } finally {
3543 try {
Christopher Tate13f4a642009-09-30 20:06:45 -07003544 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07003545 mTransport.finishBackup();
3546 } catch (RemoteException e) {
3547 // can't happen; the transport is local
3548 }
Christopher Tateb6787f22009-07-02 17:40:45 -07003549
3550 // Last but not least, release the cpu
3551 mWakelock.release();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003552 }
3553 }
3554 }
3555
Christopher Tate44a27902010-01-27 17:15:49 -08003556 class PerformInitializeTask implements Runnable {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003557 HashSet<String> mQueue;
3558
Christopher Tate44a27902010-01-27 17:15:49 -08003559 PerformInitializeTask(HashSet<String> transportNames) {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003560 mQueue = transportNames;
3561 }
3562
Christopher Tate4cc86e12009-09-21 19:36:51 -07003563 public void run() {
Christopher Tate4cc86e12009-09-21 19:36:51 -07003564 try {
3565 for (String transportName : mQueue) {
3566 IBackupTransport transport = getTransport(transportName);
3567 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003568 Slog.e(TAG, "Requested init for " + transportName + " but not found");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003569 continue;
3570 }
3571
Joe Onorato8a9b2202010-02-26 18:56:32 -08003572 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003573 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
Dan Egnor726247c2009-09-29 19:12:31 -07003574 long startRealtime = SystemClock.elapsedRealtime();
3575 int status = transport.initializeDevice();
Christopher Tate4cc86e12009-09-21 19:36:51 -07003576
Christopher Tate4cc86e12009-09-21 19:36:51 -07003577 if (status == BackupConstants.TRANSPORT_OK) {
3578 status = transport.finishBackup();
3579 }
3580
3581 // Okay, the wipe really happened. Clean up our local bookkeeping.
3582 if (status == BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003583 Slog.i(TAG, "Device init successful");
Dan Egnor726247c2009-09-29 19:12:31 -07003584 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003585 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07003586 resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003587 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003588 synchronized (mQueueLock) {
3589 recordInitPendingLocked(false, transportName);
3590 }
Dan Egnor726247c2009-09-29 19:12:31 -07003591 } else {
3592 // If this didn't work, requeue this one and try again
3593 // after a suitable interval
Joe Onorato8a9b2202010-02-26 18:56:32 -08003594 Slog.e(TAG, "Transport error in initializeDevice()");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003595 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003596 synchronized (mQueueLock) {
3597 recordInitPendingLocked(true, transportName);
3598 }
3599 // do this via another alarm to make sure of the wakelock states
3600 long delay = transport.requestBackupTime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08003601 if (DEBUG) Slog.w(TAG, "init failed on "
Christopher Tate4cc86e12009-09-21 19:36:51 -07003602 + transportName + " resched in " + delay);
3603 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
3604 System.currentTimeMillis() + delay, mRunInitIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003605 }
Christopher Tate4cc86e12009-09-21 19:36:51 -07003606 }
3607 } catch (RemoteException e) {
3608 // can't happen; the transports are local
3609 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003610 Slog.e(TAG, "Unexpected error performing init", e);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003611 } finally {
Christopher Tatec2af5d32010-02-02 15:18:58 -08003612 // Done; release the wakelock
Christopher Tate4cc86e12009-09-21 19:36:51 -07003613 mWakelock.release();
3614 }
3615 }
3616 }
3617
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003618 private void dataChangedImpl(String packageName) {
3619 HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3620 dataChangedImpl(packageName, targets);
3621 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07003622
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003623 private void dataChangedImpl(String packageName, HashSet<ApplicationInfo> targets) {
Christopher Tate487529a2009-04-29 14:03:25 -07003624 // Record that we need a backup pass for the caller. Since multiple callers
3625 // may share a uid, we need to note all candidates within that uid and schedule
3626 // a backup pass for each of them.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003627 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003628
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003629 if (targets == null) {
3630 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3631 + " uid=" + Binder.getCallingUid());
3632 return;
3633 }
3634
3635 synchronized (mQueueLock) {
3636 // Note that this client has made data changes that need to be backed up
3637 for (ApplicationInfo app : targets) {
3638 // validate the caller-supplied package name against the known set of
3639 // packages associated with this uid
3640 if (app.packageName.equals(packageName)) {
3641 // Add the caller to the set of pending backups. If there is
3642 // one already there, then overwrite it, but no harm done.
Christopher Tate4a627c72011-04-01 14:43:32 -07003643 BackupRequest req = new BackupRequest(app);
Christopher Tatec28083a2010-12-14 16:16:44 -08003644 if (mPendingBackups.put(app.packageName, req) == null) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003645 // Journal this request in case of crash. The put()
3646 // operation returned null when this package was not already
3647 // in the set; we want to avoid touching the disk redundantly.
3648 writeToJournalLocked(packageName);
3649
3650 if (DEBUG) {
3651 int numKeys = mPendingBackups.size();
3652 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
3653 for (BackupRequest b : mPendingBackups.values()) {
3654 Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
3655 }
3656 }
3657 }
3658 }
3659 }
3660 }
3661 }
3662
3663 // Note: packageName is currently unused, but may be in the future
3664 private HashSet<ApplicationInfo> dataChangedTargets(String packageName) {
Christopher Tate63d27002009-06-16 17:16:42 -07003665 // If the caller does not hold the BACKUP permission, it can only request a
3666 // backup of its own data.
Dianne Hackborncf098292009-07-01 19:55:20 -07003667 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tate63d27002009-06-16 17:16:42 -07003668 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003669 synchronized (mBackupParticipants) {
3670 return mBackupParticipants.get(Binder.getCallingUid());
3671 }
3672 }
3673
3674 // a caller with full permission can ask to back up any participating app
3675 // !!! TODO: allow backup of ANY app?
3676 HashSet<ApplicationInfo> targets = new HashSet<ApplicationInfo>();
3677 synchronized (mBackupParticipants) {
Christopher Tate63d27002009-06-16 17:16:42 -07003678 int N = mBackupParticipants.size();
3679 for (int i = 0; i < N; i++) {
3680 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3681 if (s != null) {
3682 targets.addAll(s);
3683 }
3684 }
3685 }
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003686 return targets;
Christopher Tate487529a2009-04-29 14:03:25 -07003687 }
Christopher Tate46758122009-05-06 11:22:00 -07003688
Christopher Tatecde87f42009-06-12 12:55:53 -07003689 private void writeToJournalLocked(String str) {
Dan Egnor852f8e42009-09-30 11:20:45 -07003690 RandomAccessFile out = null;
3691 try {
3692 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
3693 out = new RandomAccessFile(mJournal, "rws");
3694 out.seek(out.length());
3695 out.writeUTF(str);
3696 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003697 Slog.e(TAG, "Can't write " + str + " to backup journal", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07003698 mJournal = null;
3699 } finally {
3700 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tatecde87f42009-06-12 12:55:53 -07003701 }
3702 }
3703
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07003704 // ----- IBackupManager binder interface -----
3705
3706 public void dataChanged(final String packageName) {
3707 final HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
3708 if (targets == null) {
3709 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
3710 + " uid=" + Binder.getCallingUid());
3711 return;
3712 }
3713
3714 mBackupHandler.post(new Runnable() {
3715 public void run() {
3716 dataChangedImpl(packageName, targets);
3717 }
3718 });
3719 }
3720
Christopher Tateee0e78a2009-07-02 11:17:03 -07003721 // Clear the given package's backup data from the current transport
3722 public void clearBackupData(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003723 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003724 PackageInfo info;
3725 try {
3726 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
3727 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003728 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003729 return;
3730 }
3731
3732 // If the caller does not hold the BACKUP permission, it can only request a
3733 // wipe of its own backed-up data.
3734 HashSet<ApplicationInfo> apps;
Christopher Tate4e3e50c2009-07-02 12:14:05 -07003735 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tateee0e78a2009-07-02 11:17:03 -07003736 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
3737 apps = mBackupParticipants.get(Binder.getCallingUid());
3738 } else {
3739 // a caller with full permission can ask to back up any participating app
3740 // !!! TODO: allow data-clear of ANY app?
Joe Onorato8a9b2202010-02-26 18:56:32 -08003741 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003742 apps = new HashSet<ApplicationInfo>();
3743 int N = mBackupParticipants.size();
3744 for (int i = 0; i < N; i++) {
3745 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
3746 if (s != null) {
3747 apps.addAll(s);
3748 }
3749 }
3750 }
3751
3752 // now find the given package in the set of candidate apps
3753 for (ApplicationInfo app : apps) {
3754 if (app.packageName.equals(packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003755 if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
Christopher Tateee0e78a2009-07-02 11:17:03 -07003756 // found it; fire off the clear request
3757 synchronized (mQueueLock) {
Christopher Tateaa93b042009-08-05 18:21:40 -07003758 long oldId = Binder.clearCallingIdentity();
Christopher Tateb6787f22009-07-02 17:40:45 -07003759 mWakelock.acquire();
Christopher Tateee0e78a2009-07-02 11:17:03 -07003760 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
3761 new ClearParams(getTransport(mCurrentTransport), info));
3762 mBackupHandler.sendMessage(msg);
Christopher Tateaa93b042009-08-05 18:21:40 -07003763 Binder.restoreCallingIdentity(oldId);
Christopher Tateee0e78a2009-07-02 11:17:03 -07003764 }
3765 break;
3766 }
3767 }
3768 }
3769
Christopher Tateace7f092009-06-15 18:07:25 -07003770 // Run a backup pass immediately for any applications that have declared
3771 // that they have pending updates.
Dan Egnor852f8e42009-09-30 11:20:45 -07003772 public void backupNow() {
Joe Onorato5933a492009-07-23 18:24:08 -04003773 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
Christopher Tate043dadc2009-06-02 16:11:00 -07003774
Joe Onorato8a9b2202010-02-26 18:56:32 -08003775 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
Christopher Tate46758122009-05-06 11:22:00 -07003776 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07003777 // Because the alarms we are using can jitter, and we want an *immediate*
3778 // backup pass to happen, we restart the timer beginning with "next time,"
3779 // then manually fire the backup trigger intent ourselves.
3780 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tateb6787f22009-07-02 17:40:45 -07003781 try {
Christopher Tateb6787f22009-07-02 17:40:45 -07003782 mRunBackupIntent.send();
3783 } catch (PendingIntent.CanceledException e) {
3784 // should never happen
Joe Onorato8a9b2202010-02-26 18:56:32 -08003785 Slog.e(TAG, "run-backup intent cancelled!");
Christopher Tateb6787f22009-07-02 17:40:45 -07003786 }
Christopher Tate46758122009-05-06 11:22:00 -07003787 }
3788 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07003789
Christopher Tate4a627c72011-04-01 14:43:32 -07003790 // Run a *full* backup pass for the given package, writing the resulting data stream
3791 // to the supplied file descriptor. This method is synchronous and does not return
3792 // to the caller until the backup has been completed.
3793 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
3794 boolean doAllApps, String[] pkgList) {
3795 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3796
3797 // Validate
3798 if (!doAllApps) {
3799 if (!includeShared) {
3800 // If we're backing up shared data (sdcard or equivalent), then we can run
3801 // without any supplied app names. Otherwise, we'd be doing no work, so
3802 // report the error.
3803 if (pkgList == null || pkgList.length == 0) {
3804 throw new IllegalArgumentException(
3805 "Backup requested but neither shared nor any apps named");
3806 }
3807 }
3808 }
3809
3810 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
3811 + " shared=" + includeShared + " all=" + doAllApps
3812 + " pkgs=" + pkgList);
3813
3814 long oldId = Binder.clearCallingIdentity();
3815 try {
3816 FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
3817 doAllApps, pkgList);
3818 final int token = generateToken();
3819 synchronized (mFullConfirmations) {
3820 mFullConfirmations.put(token, params);
3821 }
3822
Christopher Tate75a99702011-05-18 16:28:19 -07003823 // start up the confirmation UI
3824 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
3825 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
3826 Slog.e(TAG, "Unable to launch full backup confirmation");
Christopher Tate4a627c72011-04-01 14:43:32 -07003827 mFullConfirmations.delete(token);
3828 return;
3829 }
Christopher Tate75a99702011-05-18 16:28:19 -07003830
3831 // make sure the screen is lit for the user interaction
Christopher Tate4a627c72011-04-01 14:43:32 -07003832 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3833
3834 // start the confirmation countdown
Christopher Tate75a99702011-05-18 16:28:19 -07003835 startConfirmationTimeout(token, params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003836
3837 // wait for the backup to be performed
3838 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
3839 waitForCompletion(params);
Christopher Tate4a627c72011-04-01 14:43:32 -07003840 } finally {
Christopher Tate4a627c72011-04-01 14:43:32 -07003841 try {
3842 fd.close();
3843 } catch (IOException e) {
3844 // just eat it
3845 }
Christopher Tate75a99702011-05-18 16:28:19 -07003846 Binder.restoreCallingIdentity(oldId);
Christopher Tate4a627c72011-04-01 14:43:32 -07003847 }
Christopher Tate75a99702011-05-18 16:28:19 -07003848 if (DEBUG) Slog.d(TAG, "Full backup done; returning to caller");
3849 }
3850
3851 public void fullRestore(ParcelFileDescriptor fd) {
3852 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3853 Slog.i(TAG, "Beginning full restore...");
3854
3855 long oldId = Binder.clearCallingIdentity();
3856
3857 try {
3858 FullRestoreParams params = new FullRestoreParams(fd);
3859 final int token = generateToken();
3860 synchronized (mFullConfirmations) {
3861 mFullConfirmations.put(token, params);
3862 }
3863
3864 // start up the confirmation UI
3865 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
3866 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
3867 Slog.e(TAG, "Unable to launch full restore confirmation");
3868 mFullConfirmations.delete(token);
3869 return;
3870 }
3871
3872 // make sure the screen is lit for the user interaction
3873 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
3874
3875 // start the confirmation countdown
3876 startConfirmationTimeout(token, params);
3877
3878 // wait for the restore to be performed
3879 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
3880 waitForCompletion(params);
3881 } finally {
3882 try {
3883 fd.close();
3884 } catch (IOException e) {
3885 Slog.w(TAG, "Error trying to close fd after full restore: " + e);
3886 }
3887 Binder.restoreCallingIdentity(oldId);
3888 Slog.i(TAG, "Full restore completed");
3889 }
3890 }
3891
3892 boolean startConfirmationUi(int token, String action) {
3893 try {
3894 Intent confIntent = new Intent(action);
3895 confIntent.setClassName("com.android.backupconfirm",
3896 "com.android.backupconfirm.BackupRestoreConfirmation");
3897 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
3898 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3899 mContext.startActivity(confIntent);
3900 } catch (ActivityNotFoundException e) {
3901 return false;
3902 }
3903 return true;
3904 }
3905
3906 void startConfirmationTimeout(int token, FullParams params) {
3907 if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
3908 + TIMEOUT_FULL_CONFIRMATION + " millis");
3909 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
3910 token, 0, params);
3911 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
Christopher Tate4a627c72011-04-01 14:43:32 -07003912 }
3913
3914 void waitForCompletion(FullParams params) {
3915 synchronized (params.latch) {
3916 while (params.latch.get() == false) {
3917 try {
3918 params.latch.wait();
3919 } catch (InterruptedException e) { /* never interrupted */ }
3920 }
3921 }
3922 }
3923
3924 void signalFullBackupRestoreCompletion(FullParams params) {
3925 synchronized (params.latch) {
3926 params.latch.set(true);
3927 params.latch.notifyAll();
3928 }
3929 }
3930
3931 // Confirm that the previously-requested full backup/restore operation can proceed. This
3932 // is used to require a user-facing disclosure about the operation.
3933 public void acknowledgeFullBackupOrRestore(int token, boolean allow,
3934 IFullBackupRestoreObserver observer) {
3935 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
3936 + " allow=" + allow);
3937
3938 // TODO: possibly require not just this signature-only permission, but even
3939 // require that the specific designated confirmation-UI app uid is the caller?
3940 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
3941
3942 long oldId = Binder.clearCallingIdentity();
3943 try {
3944
3945 FullParams params;
3946 synchronized (mFullConfirmations) {
3947 params = mFullConfirmations.get(token);
3948 if (params != null) {
3949 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
3950 mFullConfirmations.delete(token);
3951
3952 if (allow) {
3953 params.observer = observer;
3954 final int verb = params instanceof FullBackupParams
Christopher Tate75a99702011-05-18 16:28:19 -07003955 ? MSG_RUN_FULL_BACKUP
Christopher Tate4a627c72011-04-01 14:43:32 -07003956 : MSG_RUN_FULL_RESTORE;
3957
Christopher Tate75a99702011-05-18 16:28:19 -07003958 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
Christopher Tate4a627c72011-04-01 14:43:32 -07003959 mWakelock.acquire();
3960 Message msg = mBackupHandler.obtainMessage(verb, params);
3961 mBackupHandler.sendMessage(msg);
3962 } else {
3963 Slog.w(TAG, "User rejected full backup/restore operation");
3964 // indicate completion without having actually transferred any data
3965 signalFullBackupRestoreCompletion(params);
3966 }
3967 } else {
3968 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
3969 }
3970 }
3971 } finally {
3972 Binder.restoreCallingIdentity(oldId);
3973 }
3974 }
3975
Christopher Tate8031a3d2009-07-06 16:36:05 -07003976 // Enable/disable the backup service
Christopher Tate6ef58a12009-06-29 14:56:28 -07003977 public void setBackupEnabled(boolean enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003978 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4a627c72011-04-01 14:43:32 -07003979 "setBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07003980
Joe Onorato8a9b2202010-02-26 18:56:32 -08003981 Slog.i(TAG, "Backup enabled => " + enable);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003982
Christopher Tate6ef58a12009-06-29 14:56:28 -07003983 boolean wasEnabled = mEnabled;
3984 synchronized (this) {
Dianne Hackborncf098292009-07-01 19:55:20 -07003985 Settings.Secure.putInt(mContext.getContentResolver(),
3986 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
Christopher Tate6ef58a12009-06-29 14:56:28 -07003987 mEnabled = enable;
3988 }
3989
Christopher Tate49401dd2009-07-01 12:34:29 -07003990 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07003991 if (enable && !wasEnabled && mProvisioned) {
Christopher Tate49401dd2009-07-01 12:34:29 -07003992 // if we've just been enabled, start scheduling backup passes
Christopher Tate8031a3d2009-07-06 16:36:05 -07003993 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tate49401dd2009-07-01 12:34:29 -07003994 } else if (!enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07003995 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08003996 if (DEBUG) Slog.i(TAG, "Opting out of backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -07003997
Christopher Tateb6787f22009-07-02 17:40:45 -07003998 mAlarmManager.cancel(mRunBackupIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07003999
4000 // This also constitutes an opt-out, so we wipe any data for
4001 // this device from the backend. We start that process with
4002 // an alarm in order to guarantee wakelock states.
4003 if (wasEnabled && mProvisioned) {
4004 // NOTE: we currently flush every registered transport, not just
4005 // the currently-active one.
4006 HashSet<String> allTransports;
4007 synchronized (mTransports) {
4008 allTransports = new HashSet<String>(mTransports.keySet());
4009 }
4010 // build the set of transports for which we are posting an init
4011 for (String transport : allTransports) {
4012 recordInitPendingLocked(true, transport);
4013 }
4014 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
4015 mRunInitIntent);
4016 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07004017 }
4018 }
Christopher Tate49401dd2009-07-01 12:34:29 -07004019 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07004020
Christopher Tatecce9da52010-02-03 15:11:15 -08004021 // Enable/disable automatic restore of app data at install time
4022 public void setAutoRestore(boolean doAutoRestore) {
4023 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4024 "setBackupEnabled");
4025
Joe Onorato8a9b2202010-02-26 18:56:32 -08004026 Slog.i(TAG, "Auto restore => " + doAutoRestore);
Christopher Tatecce9da52010-02-03 15:11:15 -08004027
4028 synchronized (this) {
4029 Settings.Secure.putInt(mContext.getContentResolver(),
4030 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
4031 mAutoRestore = doAutoRestore;
4032 }
4033 }
4034
Christopher Tate8031a3d2009-07-06 16:36:05 -07004035 // Mark the backup service as having been provisioned
4036 public void setBackupProvisioned(boolean available) {
4037 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4038 "setBackupProvisioned");
4039
4040 boolean wasProvisioned = mProvisioned;
4041 synchronized (this) {
4042 Settings.Secure.putInt(mContext.getContentResolver(),
4043 Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
4044 mProvisioned = available;
4045 }
4046
4047 synchronized (mQueueLock) {
4048 if (available && !wasProvisioned && mEnabled) {
4049 // we're now good to go, so start the backup alarms
4050 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
4051 } else if (!available) {
4052 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08004053 Slog.w(TAG, "Backup service no longer provisioned");
Christopher Tate8031a3d2009-07-06 16:36:05 -07004054 mAlarmManager.cancel(mRunBackupIntent);
4055 }
4056 }
4057 }
4058
4059 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
Dan Egnorc1c49c02009-10-30 17:35:39 -07004060 // We used to use setInexactRepeating(), but that may be linked to
4061 // backups running at :00 more often than not, creating load spikes.
4062 // Schedule at an exact time for now, and also add a bit of "fuzz".
4063
4064 Random random = new Random();
4065 long when = System.currentTimeMillis() + delayBeforeFirstBackup +
4066 random.nextInt(FUZZ_MILLIS);
4067 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
4068 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
Christopher Tate55f931a2009-09-29 17:17:34 -07004069 mNextBackupPass = when;
Christopher Tate8031a3d2009-07-06 16:36:05 -07004070 }
4071
Christopher Tate6ef58a12009-06-29 14:56:28 -07004072 // Report whether the backup mechanism is currently enabled
4073 public boolean isBackupEnabled() {
Joe Onorato5933a492009-07-23 18:24:08 -04004074 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07004075 return mEnabled; // no need to synchronize just to read it
4076 }
4077
Christopher Tate91717492009-06-26 21:07:13 -07004078 // Report the name of the currently active transport
4079 public String getCurrentTransport() {
Joe Onorato5933a492009-07-23 18:24:08 -04004080 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4e3e50c2009-07-02 12:14:05 -07004081 "getCurrentTransport");
Joe Onorato8a9b2202010-02-26 18:56:32 -08004082 if (DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -07004083 return mCurrentTransport;
Christopher Tateace7f092009-06-15 18:07:25 -07004084 }
4085
Christopher Tate91717492009-06-26 21:07:13 -07004086 // Report all known, available backup transports
4087 public String[] listAllTransports() {
Christopher Tate34ebd0e2009-07-06 15:44:54 -07004088 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
Christopher Tate043dadc2009-06-02 16:11:00 -07004089
Christopher Tate91717492009-06-26 21:07:13 -07004090 String[] list = null;
4091 ArrayList<String> known = new ArrayList<String>();
4092 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
4093 if (entry.getValue() != null) {
4094 known.add(entry.getKey());
4095 }
4096 }
4097
4098 if (known.size() > 0) {
4099 list = new String[known.size()];
4100 known.toArray(list);
4101 }
4102 return list;
4103 }
4104
4105 // Select which transport to use for the next backup operation. If the given
4106 // name is not one of the available transports, no action is taken and the method
4107 // returns null.
4108 public String selectBackupTransport(String transport) {
Joe Onorato5933a492009-07-23 18:24:08 -04004109 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
Christopher Tate91717492009-06-26 21:07:13 -07004110
4111 synchronized (mTransports) {
4112 String prevTransport = null;
4113 if (mTransports.get(transport) != null) {
4114 prevTransport = mCurrentTransport;
4115 mCurrentTransport = transport;
Dianne Hackborncf098292009-07-01 19:55:20 -07004116 Settings.Secure.putString(mContext.getContentResolver(),
4117 Settings.Secure.BACKUP_TRANSPORT, transport);
Joe Onorato8a9b2202010-02-26 18:56:32 -08004118 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
Christopher Tate91717492009-06-26 21:07:13 -07004119 + " returning " + prevTransport);
4120 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004121 Slog.w(TAG, "Attempt to select unavailable transport " + transport);
Christopher Tate91717492009-06-26 21:07:13 -07004122 }
4123 return prevTransport;
4124 }
Christopher Tate043dadc2009-06-02 16:11:00 -07004125 }
4126
Christopher Tatef5e1c292010-12-08 18:40:26 -08004127 // Supply the configuration Intent for the given transport. If the name is not one
4128 // of the available transports, or if the transport does not supply any configuration
4129 // UI, the method returns null.
4130 public Intent getConfigurationIntent(String transportName) {
4131 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4132 "getConfigurationIntent");
4133
4134 synchronized (mTransports) {
4135 final IBackupTransport transport = mTransports.get(transportName);
4136 if (transport != null) {
4137 try {
4138 final Intent intent = transport.configurationIntent();
4139 if (DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
4140 + intent);
4141 return intent;
4142 } catch (RemoteException e) {
4143 /* fall through to return null */
4144 }
4145 }
4146 }
4147
4148 return null;
4149 }
4150
4151 // Supply the configuration summary string for the given transport. If the name is
4152 // not one of the available transports, or if the transport does not supply any
4153 // summary / destination string, the method can return null.
4154 //
4155 // This string is used VERBATIM as the summary text of the relevant Settings item!
4156 public String getDestinationString(String transportName) {
4157 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4158 "getConfigurationIntent");
4159
4160 synchronized (mTransports) {
4161 final IBackupTransport transport = mTransports.get(transportName);
4162 if (transport != null) {
4163 try {
4164 final String text = transport.currentDestinationString();
4165 if (DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
4166 return text;
4167 } catch (RemoteException e) {
4168 /* fall through to return null */
4169 }
4170 }
4171 }
4172
4173 return null;
4174 }
4175
Christopher Tate043dadc2009-06-02 16:11:00 -07004176 // Callback: a requested backup agent has been instantiated. This should only
4177 // be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07004178 public void agentConnected(String packageName, IBinder agentBinder) {
Christopher Tate043dadc2009-06-02 16:11:00 -07004179 synchronized(mAgentConnectLock) {
4180 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004181 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
Christopher Tate043dadc2009-06-02 16:11:00 -07004182 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
4183 mConnectedAgent = agent;
4184 mConnecting = false;
4185 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004186 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07004187 + " claiming agent connected");
4188 }
4189 mAgentConnectLock.notifyAll();
4190 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004191 }
4192
4193 // Callback: a backup agent has failed to come up, or has unexpectedly quit.
4194 // If the agent failed to come up in the first place, the agentBinder argument
Christopher Tate043dadc2009-06-02 16:11:00 -07004195 // will be null. This should only be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07004196 public void agentDisconnected(String packageName) {
4197 // TODO: handle backup being interrupted
Christopher Tate043dadc2009-06-02 16:11:00 -07004198 synchronized(mAgentConnectLock) {
4199 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
4200 mConnectedAgent = null;
4201 mConnecting = false;
4202 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004203 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07004204 + " claiming agent disconnected");
4205 }
4206 mAgentConnectLock.notifyAll();
4207 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004208 }
Christopher Tate181fafa2009-05-14 11:12:14 -07004209
Christopher Tate1bb69062010-02-19 17:02:12 -08004210 // An application being installed will need a restore pass, then the Package Manager
4211 // will need to be told when the restore is finished.
4212 public void restoreAtInstall(String packageName, int token) {
4213 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004214 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate1bb69062010-02-19 17:02:12 -08004215 + " attemping install-time restore");
4216 return;
4217 }
4218
4219 long restoreSet = getAvailableRestoreToken(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08004220 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
Christopher Tate1bb69062010-02-19 17:02:12 -08004221 + " token=" + Integer.toHexString(token));
4222
Christopher Tatef0872722010-02-25 15:22:48 -08004223 if (mAutoRestore && mProvisioned && restoreSet != 0) {
Christopher Tate1bb69062010-02-19 17:02:12 -08004224 // okay, we're going to attempt a restore of this package from this restore set.
4225 // The eventual message back into the Package Manager to run the post-install
4226 // steps for 'token' will be issued from the restore handling code.
4227
4228 // We can use a synthetic PackageInfo here because:
4229 // 1. We know it's valid, since the Package Manager supplied the name
4230 // 2. Only the packageName field will be used by the restore code
4231 PackageInfo pkg = new PackageInfo();
4232 pkg.packageName = packageName;
4233
4234 mWakelock.acquire();
4235 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
4236 msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
Chris Tate249345b2010-10-29 12:57:04 -07004237 restoreSet, pkg, token, true);
Christopher Tate1bb69062010-02-19 17:02:12 -08004238 mBackupHandler.sendMessage(msg);
4239 } else {
Christopher Tatef0872722010-02-25 15:22:48 -08004240 // Auto-restore disabled or no way to attempt a restore; just tell the Package
4241 // Manager to proceed with the post-install handling for this package.
Joe Onorato8a9b2202010-02-26 18:56:32 -08004242 if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore");
Christopher Tate1bb69062010-02-19 17:02:12 -08004243 try {
4244 mPackageManagerBinder.finishPackageInstall(token);
4245 } catch (RemoteException e) { /* can't happen */ }
4246 }
4247 }
4248
Christopher Tate8c850b72009-06-07 19:33:20 -07004249 // Hand off a restore session
Chris Tate44ab8452010-11-16 15:10:49 -08004250 public IRestoreSession beginRestoreSession(String packageName, String transport) {
4251 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
4252 + " transport=" + transport);
4253
4254 boolean needPermission = true;
4255 if (transport == null) {
4256 transport = mCurrentTransport;
4257
4258 if (packageName != null) {
4259 PackageInfo app = null;
4260 try {
4261 app = mPackageManager.getPackageInfo(packageName, 0);
4262 } catch (NameNotFoundException nnf) {
4263 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
4264 throw new IllegalArgumentException("Package " + packageName + " not found");
4265 }
4266
4267 if (app.applicationInfo.uid == Binder.getCallingUid()) {
4268 // So: using the current active transport, and the caller has asked
4269 // that its own package will be restored. In this narrow use case
4270 // we do not require the caller to hold the permission.
4271 needPermission = false;
4272 }
4273 }
4274 }
4275
4276 if (needPermission) {
4277 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4278 "beginRestoreSession");
4279 } else {
4280 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
4281 }
Christopher Tatef68eb502009-06-16 11:02:01 -07004282
4283 synchronized(this) {
4284 if (mActiveRestoreSession != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004285 Slog.d(TAG, "Restore session requested but one already active");
Christopher Tatef68eb502009-06-16 11:02:01 -07004286 return null;
4287 }
Chris Tate44ab8452010-11-16 15:10:49 -08004288 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
Christopher Tate73a3cb32010-12-13 18:27:26 -08004289 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
Christopher Tatef68eb502009-06-16 11:02:01 -07004290 }
4291 return mActiveRestoreSession;
Christopher Tate8c850b72009-06-07 19:33:20 -07004292 }
Christopher Tate043dadc2009-06-02 16:11:00 -07004293
Christopher Tate73a3cb32010-12-13 18:27:26 -08004294 void clearRestoreSession(ActiveRestoreSession currentSession) {
4295 synchronized(this) {
4296 if (currentSession != mActiveRestoreSession) {
4297 Slog.e(TAG, "ending non-current restore session");
4298 } else {
4299 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
4300 mActiveRestoreSession = null;
4301 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
4302 }
4303 }
4304 }
4305
Christopher Tate44a27902010-01-27 17:15:49 -08004306 // Note that a currently-active backup agent has notified us that it has
4307 // completed the given outstanding asynchronous backup/restore operation.
4308 public void opComplete(int token) {
4309 synchronized (mCurrentOpLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004310 if (DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
Christopher Tate44a27902010-01-27 17:15:49 -08004311 mCurrentOperations.put(token, OP_ACKNOWLEDGED);
4312 mCurrentOpLock.notifyAll();
4313 }
4314 }
4315
Christopher Tate9b3905c2009-06-08 15:24:01 -07004316 // ----- Restore session -----
4317
Christopher Tate80202c82010-01-25 19:37:47 -08004318 class ActiveRestoreSession extends IRestoreSession.Stub {
Christopher Tatef68eb502009-06-16 11:02:01 -07004319 private static final String TAG = "RestoreSession";
4320
Chris Tate44ab8452010-11-16 15:10:49 -08004321 private String mPackageName;
Christopher Tate9b3905c2009-06-08 15:24:01 -07004322 private IBackupTransport mRestoreTransport = null;
4323 RestoreSet[] mRestoreSets = null;
Christopher Tate73a3cb32010-12-13 18:27:26 -08004324 boolean mEnded = false;
Christopher Tate9b3905c2009-06-08 15:24:01 -07004325
Chris Tate44ab8452010-11-16 15:10:49 -08004326 ActiveRestoreSession(String packageName, String transport) {
4327 mPackageName = packageName;
Christopher Tate91717492009-06-26 21:07:13 -07004328 mRestoreTransport = getTransport(transport);
Christopher Tate9b3905c2009-06-08 15:24:01 -07004329 }
4330
4331 // --- Binder interface ---
Christopher Tate2d449afe2010-03-29 19:14:24 -07004332 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
Joe Onorato5933a492009-07-23 18:24:08 -04004333 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004334 "getAvailableRestoreSets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004335 if (observer == null) {
4336 throw new IllegalArgumentException("Observer must not be null");
4337 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004338
Christopher Tate73a3cb32010-12-13 18:27:26 -08004339 if (mEnded) {
4340 throw new IllegalStateException("Restore session already ended");
4341 }
4342
Christopher Tate1bb69062010-02-19 17:02:12 -08004343 long oldId = Binder.clearCallingIdentity();
Christopher Tatef68eb502009-06-16 11:02:01 -07004344 try {
Christopher Tate43383042009-07-13 15:17:13 -07004345 if (mRestoreTransport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004346 Slog.w(TAG, "Null transport getting restore sets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07004347 return -1;
Dan Egnor0084da52009-07-29 12:57:16 -07004348 }
Christopher Tate2d449afe2010-03-29 19:14:24 -07004349 // spin off the transport request to our service thread
4350 mWakelock.acquire();
4351 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
4352 new RestoreGetSetsParams(mRestoreTransport, this, observer));
4353 mBackupHandler.sendMessage(msg);
4354 return 0;
Dan Egnor0084da52009-07-29 12:57:16 -07004355 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004356 Slog.e(TAG, "Error in getAvailableRestoreSets", e);
Christopher Tate2d449afe2010-03-29 19:14:24 -07004357 return -1;
Christopher Tate1bb69062010-02-19 17:02:12 -08004358 } finally {
4359 Binder.restoreCallingIdentity(oldId);
Christopher Tatef68eb502009-06-16 11:02:01 -07004360 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07004361 }
4362
Christopher Tate84725812010-02-04 15:52:40 -08004363 public synchronized int restoreAll(long token, IRestoreObserver observer) {
Dan Egnor0084da52009-07-29 12:57:16 -07004364 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4365 "performRestore");
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004366
Chris Tate44ab8452010-11-16 15:10:49 -08004367 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
Christopher Tatef2c321a2009-08-10 15:43:36 -07004368 + " observer=" + observer);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004369
Christopher Tate73a3cb32010-12-13 18:27:26 -08004370 if (mEnded) {
4371 throw new IllegalStateException("Restore session already ended");
4372 }
4373
Dan Egnor0084da52009-07-29 12:57:16 -07004374 if (mRestoreTransport == null || mRestoreSets == null) {
Chris Tate44ab8452010-11-16 15:10:49 -08004375 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
4376 return -1;
4377 }
4378
4379 if (mPackageName != null) {
4380 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
Dan Egnor0084da52009-07-29 12:57:16 -07004381 return -1;
4382 }
4383
Christopher Tate21ab6a52009-09-24 18:01:46 -07004384 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07004385 for (int i = 0; i < mRestoreSets.length; i++) {
4386 if (token == mRestoreSets[i].token) {
4387 long oldId = Binder.clearCallingIdentity();
Christopher Tate21ab6a52009-09-24 18:01:46 -07004388 mWakelock.acquire();
4389 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004390 msg.obj = new RestoreParams(mRestoreTransport, observer, token, true);
Christopher Tate21ab6a52009-09-24 18:01:46 -07004391 mBackupHandler.sendMessage(msg);
4392 Binder.restoreCallingIdentity(oldId);
4393 return 0;
4394 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004395 }
4396 }
Christopher Tate0e0b4ae2009-08-10 16:13:47 -07004397
Joe Onorato8a9b2202010-02-26 18:56:32 -08004398 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
Christopher Tate9b3905c2009-06-08 15:24:01 -07004399 return -1;
4400 }
4401
Christopher Tate284f1bb2011-07-07 14:31:18 -07004402 public synchronized int restoreSome(long token, IRestoreObserver observer,
4403 String[] packages) {
4404 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
4405 "performRestore");
4406
4407 if (DEBUG) {
4408 StringBuilder b = new StringBuilder(128);
4409 b.append("restoreSome token=");
4410 b.append(Long.toHexString(token));
4411 b.append(" observer=");
4412 b.append(observer.toString());
4413 b.append(" packages=");
4414 if (packages == null) {
4415 b.append("null");
4416 } else {
4417 b.append('{');
4418 boolean first = true;
4419 for (String s : packages) {
4420 if (!first) {
4421 b.append(", ");
4422 } else first = false;
4423 b.append(s);
4424 }
4425 b.append('}');
4426 }
4427 Slog.d(TAG, b.toString());
4428 }
4429
4430 if (mEnded) {
4431 throw new IllegalStateException("Restore session already ended");
4432 }
4433
4434 if (mRestoreTransport == null || mRestoreSets == null) {
4435 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
4436 return -1;
4437 }
4438
4439 if (mPackageName != null) {
4440 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
4441 return -1;
4442 }
4443
4444 synchronized (mQueueLock) {
4445 for (int i = 0; i < mRestoreSets.length; i++) {
4446 if (token == mRestoreSets[i].token) {
4447 long oldId = Binder.clearCallingIdentity();
4448 mWakelock.acquire();
4449 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
4450 msg.obj = new RestoreParams(mRestoreTransport, observer, token,
4451 packages, true);
4452 mBackupHandler.sendMessage(msg);
4453 Binder.restoreCallingIdentity(oldId);
4454 return 0;
4455 }
4456 }
4457 }
4458
4459 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
4460 return -1;
4461 }
4462
Christopher Tate84725812010-02-04 15:52:40 -08004463 public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004464 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
Christopher Tate84725812010-02-04 15:52:40 -08004465
Christopher Tate73a3cb32010-12-13 18:27:26 -08004466 if (mEnded) {
4467 throw new IllegalStateException("Restore session already ended");
4468 }
4469
Chris Tate44ab8452010-11-16 15:10:49 -08004470 if (mPackageName != null) {
4471 if (! mPackageName.equals(packageName)) {
4472 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
4473 + " on session for package " + mPackageName);
4474 return -1;
4475 }
4476 }
4477
Christopher Tate84725812010-02-04 15:52:40 -08004478 PackageInfo app = null;
4479 try {
4480 app = mPackageManager.getPackageInfo(packageName, 0);
4481 } catch (NameNotFoundException nnf) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004482 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004483 return -1;
4484 }
4485
4486 // If the caller is not privileged and is not coming from the target
4487 // app's uid, throw a permission exception back to the caller.
4488 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
4489 Binder.getCallingPid(), Binder.getCallingUid());
4490 if ((perm == PackageManager.PERMISSION_DENIED) &&
4491 (app.applicationInfo.uid != Binder.getCallingUid())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004492 Slog.w(TAG, "restorePackage: bad packageName=" + packageName
Christopher Tate84725812010-02-04 15:52:40 -08004493 + " or calling uid=" + Binder.getCallingUid());
4494 throw new SecurityException("No permission to restore other packages");
4495 }
4496
Christopher Tate7d411a32010-02-26 11:27:08 -08004497 // If the package has no backup agent, we obviously cannot proceed
4498 if (app.applicationInfo.backupAgentName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004499 Slog.w(TAG, "Asked to restore package " + packageName + " with no agent");
Christopher Tate7d411a32010-02-26 11:27:08 -08004500 return -1;
4501 }
4502
Christopher Tate84725812010-02-04 15:52:40 -08004503 // So far so good; we're allowed to try to restore this package. Now
4504 // check whether there is data for it in the current dataset, falling back
4505 // to the ancestral dataset if not.
Christopher Tate1bb69062010-02-19 17:02:12 -08004506 long token = getAvailableRestoreToken(packageName);
Christopher Tate84725812010-02-04 15:52:40 -08004507
4508 // If we didn't come up with a place to look -- no ancestral dataset and
4509 // the app has never been backed up from this device -- there's nothing
4510 // to do but return failure.
4511 if (token == 0) {
Chris Tate44ab8452010-11-16 15:10:49 -08004512 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
Christopher Tate84725812010-02-04 15:52:40 -08004513 return -1;
4514 }
4515
4516 // Ready to go: enqueue the restore request and claim success
4517 long oldId = Binder.clearCallingIdentity();
4518 mWakelock.acquire();
4519 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07004520 msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0, false);
Christopher Tate84725812010-02-04 15:52:40 -08004521 mBackupHandler.sendMessage(msg);
4522 Binder.restoreCallingIdentity(oldId);
4523 return 0;
4524 }
4525
Christopher Tate73a3cb32010-12-13 18:27:26 -08004526 // Posted to the handler to tear down a restore session in a cleanly synchronized way
4527 class EndRestoreRunnable implements Runnable {
4528 BackupManagerService mBackupManager;
4529 ActiveRestoreSession mSession;
4530
4531 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
4532 mBackupManager = manager;
4533 mSession = session;
4534 }
4535
4536 public void run() {
4537 // clean up the session's bookkeeping
4538 synchronized (mSession) {
4539 try {
4540 if (mSession.mRestoreTransport != null) {
4541 mSession.mRestoreTransport.finishRestore();
4542 }
4543 } catch (Exception e) {
4544 Slog.e(TAG, "Error in finishRestore", e);
4545 } finally {
4546 mSession.mRestoreTransport = null;
4547 mSession.mEnded = true;
4548 }
4549 }
4550
4551 // clean up the BackupManagerService side of the bookkeeping
4552 // and cancel any pending timeout message
4553 mBackupManager.clearRestoreSession(mSession);
4554 }
4555 }
4556
Dan Egnor0084da52009-07-29 12:57:16 -07004557 public synchronized void endRestoreSession() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004558 if (DEBUG) Slog.d(TAG, "endRestoreSession");
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004559
Christopher Tate73a3cb32010-12-13 18:27:26 -08004560 if (mEnded) {
4561 throw new IllegalStateException("Restore session already ended");
Dan Egnor0084da52009-07-29 12:57:16 -07004562 }
4563
Christopher Tate73a3cb32010-12-13 18:27:26 -08004564 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
Christopher Tate9b3905c2009-06-08 15:24:01 -07004565 }
4566 }
4567
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004568 @Override
4569 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004570 long identityToken = Binder.clearCallingIdentity();
4571 try {
4572 dumpInternal(pw);
4573 } finally {
4574 Binder.restoreCallingIdentity(identityToken);
4575 }
4576 }
4577
4578 private void dumpInternal(PrintWriter pw) {
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004579 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07004580 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
Christopher Tate55f931a2009-09-29 17:17:34 -07004581 + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
Christopher Tatec2af5d32010-02-02 15:18:58 -08004582 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
Christopher Tateae06ed92010-02-25 17:13:28 -08004583 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
Christopher Tate55f931a2009-09-29 17:17:34 -07004584 pw.println("Last backup pass: " + mLastBackupPass
4585 + " (now = " + System.currentTimeMillis() + ')');
4586 pw.println(" next scheduled: " + mNextBackupPass);
4587
Christopher Tate91717492009-06-26 21:07:13 -07004588 pw.println("Available transports:");
4589 for (String t : listAllTransports()) {
Dan Egnor852f8e42009-09-30 11:20:45 -07004590 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
4591 try {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004592 IBackupTransport transport = getTransport(t);
4593 File dir = new File(mBaseStateDir, transport.transportDirName());
4594 pw.println(" destination: " + transport.currentDestinationString());
4595 pw.println(" intent: " + transport.configurationIntent());
Dan Egnor852f8e42009-09-30 11:20:45 -07004596 for (File f : dir.listFiles()) {
4597 pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
4598 }
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08004599 } catch (Exception e) {
4600 Slog.e(TAG, "Error in transport", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07004601 pw.println(" Error: " + e);
4602 }
Christopher Tate91717492009-06-26 21:07:13 -07004603 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004604
4605 pw.println("Pending init: " + mPendingInits.size());
4606 for (String s : mPendingInits) {
4607 pw.println(" " + s);
4608 }
4609
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004610 int N = mBackupParticipants.size();
Christopher Tate55f931a2009-09-29 17:17:34 -07004611 pw.println("Participants:");
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004612 for (int i=0; i<N; i++) {
4613 int uid = mBackupParticipants.keyAt(i);
4614 pw.print(" uid: ");
4615 pw.println(uid);
Christopher Tate181fafa2009-05-14 11:12:14 -07004616 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
4617 for (ApplicationInfo app: participants) {
Christopher Tate55f931a2009-09-29 17:17:34 -07004618 pw.println(" " + app.packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004619 }
4620 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004621
Christopher Tateb49ceb32010-02-08 16:22:24 -08004622 pw.println("Ancestral packages: "
4623 + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
Christopher Tate5923c972010-04-04 17:45:35 -07004624 if (mAncestralPackages != null) {
4625 for (String pkg : mAncestralPackages) {
4626 pw.println(" " + pkg);
4627 }
Christopher Tateb49ceb32010-02-08 16:22:24 -08004628 }
4629
Christopher Tate73e02522009-07-15 14:18:26 -07004630 pw.println("Ever backed up: " + mEverStoredApps.size());
4631 for (String pkg : mEverStoredApps) {
4632 pw.println(" " + pkg);
4633 }
Christopher Tate55f931a2009-09-29 17:17:34 -07004634
4635 pw.println("Pending backup: " + mPendingBackups.size());
Christopher Tate6aa41f42009-06-19 14:14:22 -07004636 for (BackupRequest req : mPendingBackups.values()) {
Christopher Tate6ef58a12009-06-29 14:56:28 -07004637 pw.println(" " + req);
Christopher Tate181fafa2009-05-14 11:12:14 -07004638 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004639 }
4640 }
Christopher Tate487529a2009-04-29 14:03:25 -07004641}