blob: a3768c62f5a52b71e404a1356a8f9b416b52cbf7 [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 Tated2c0cd42011-09-15 15:51:29 -070037import android.content.ContentResolver;
Christopher Tate487529a2009-04-29 14:03:25 -070038import android.content.Context;
39import android.content.Intent;
Christopher Tate3799bc22009-05-06 16:13:56 -070040import android.content.IntentFilter;
Dan Egnor87a02bc2009-06-17 02:30:10 -070041import android.content.ServiceConnection;
Christopher Tate181fafa2009-05-14 11:12:14 -070042import android.content.pm.ApplicationInfo;
Christopher Tatec7b31e32009-06-10 15:49:30 -070043import android.content.pm.IPackageDataObserver;
Christopher Tatea858cb02011-06-03 12:27:51 -070044import android.content.pm.IPackageDeleteObserver;
Christopher Tate75a99702011-05-18 16:28:19 -070045import android.content.pm.IPackageInstallObserver;
Christopher Tate1bb69062010-02-19 17:02:12 -080046import android.content.pm.IPackageManager;
Christopher Tate7b881282009-06-07 13:52:37 -070047import android.content.pm.PackageInfo;
Dan Egnor87a02bc2009-06-17 02:30:10 -070048import android.content.pm.PackageManager;
Jason parks1125d782011-01-12 09:47:26 -060049import android.content.pm.Signature;
Jason parksa3cdaa52011-01-13 14:15:43 -060050import android.content.pm.PackageManager.NameNotFoundException;
Christopher Tate3799bc22009-05-06 16:13:56 -070051import android.net.Uri;
Christopher Tate487529a2009-04-29 14:03:25 -070052import android.os.Binder;
Christopher Tate75a99702011-05-18 16:28:19 -070053import android.os.Build;
Christopher Tate3799bc22009-05-06 16:13:56 -070054import android.os.Bundle;
Christopher Tate22b87872009-05-04 16:41:53 -070055import android.os.Environment;
Christopher Tate487529a2009-04-29 14:03:25 -070056import android.os.Handler;
Christopher Tate44a27902010-01-27 17:15:49 -080057import android.os.HandlerThread;
Christopher Tate487529a2009-04-29 14:03:25 -070058import android.os.IBinder;
Christopher Tate44a27902010-01-27 17:15:49 -080059import android.os.Looper;
Christopher Tate487529a2009-04-29 14:03:25 -070060import android.os.Message;
Christopher Tate22b87872009-05-04 16:41:53 -070061import android.os.ParcelFileDescriptor;
Christopher Tateb6787f22009-07-02 17:40:45 -070062import android.os.PowerManager;
Christopher Tate043dadc2009-06-02 16:11:00 -070063import android.os.Process;
Christopher Tate487529a2009-04-29 14:03:25 -070064import android.os.RemoteException;
Christopher Tate32418be2011-10-10 13:51:12 -070065import android.os.ServiceManager;
Dan Egnorbb9001c2009-07-27 12:20:13 -070066import android.os.SystemClock;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070067import android.os.WorkSource;
Christopher Tate32418be2011-10-10 13:51:12 -070068import android.os.storage.IMountService;
Oscar Montemayora8529f62009-11-18 10:14:20 -080069import android.provider.Settings;
Dan Egnorbb9001c2009-07-27 12:20:13 -070070import android.util.EventLog;
Christopher Tate79ec80d2011-06-24 14:58:49 -070071import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080072import android.util.Slog;
Christopher Tate487529a2009-04-29 14:03:25 -070073import android.util.SparseArray;
Christopher Tate4a627c72011-04-01 14:43:32 -070074import android.util.StringBuilderPrinter;
75
Jason parksa3cdaa52011-01-13 14:15:43 -060076import com.android.internal.backup.BackupConstants;
77import com.android.internal.backup.IBackupTransport;
78import com.android.internal.backup.LocalTransport;
79import com.android.server.PackageManagerBackupAgent.Metadata;
80
Christopher Tate2efd2db2011-07-19 16:32:49 -070081import java.io.BufferedInputStream;
82import java.io.BufferedOutputStream;
83import java.io.ByteArrayOutputStream;
Christopher Tate7926a692011-07-11 11:31:57 -070084import java.io.DataInputStream;
Christopher Tate2efd2db2011-07-19 16:32:49 -070085import java.io.DataOutputStream;
Christopher Tatecde87f42009-06-12 12:55:53 -070086import java.io.EOFException;
Christopher Tate22b87872009-05-04 16:41:53 -070087import java.io.File;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070088import java.io.FileDescriptor;
Christopher Tate75a99702011-05-18 16:28:19 -070089import java.io.FileInputStream;
Christopher Tate1168baa2010-02-17 13:03:40 -080090import java.io.FileNotFoundException;
Christopher Tate4cc86e12009-09-21 19:36:51 -070091import java.io.FileOutputStream;
Christopher Tatec7b31e32009-06-10 15:49:30 -070092import java.io.IOException;
Christopher Tate75a99702011-05-18 16:28:19 -070093import java.io.InputStream;
Christopher Tate2efd2db2011-07-19 16:32:49 -070094import java.io.OutputStream;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070095import java.io.PrintWriter;
Christopher Tatecde87f42009-06-12 12:55:53 -070096import java.io.RandomAccessFile;
Christopher Tate2efd2db2011-07-19 16:32:49 -070097import java.security.InvalidAlgorithmParameterException;
98import java.security.InvalidKeyException;
99import java.security.Key;
100import java.security.NoSuchAlgorithmException;
101import java.security.SecureRandom;
102import java.security.spec.InvalidKeySpecException;
103import java.security.spec.KeySpec;
Christopher Tate75a99702011-05-18 16:28:19 -0700104import java.text.SimpleDateFormat;
Joe Onorato8ad02812009-05-13 01:41:44 -0400105import java.util.ArrayList;
Christopher Tate7bdb0962011-07-13 19:30:21 -0700106import java.util.Arrays;
Christopher Tate75a99702011-05-18 16:28:19 -0700107import java.util.Date;
Joe Onorato8ad02812009-05-13 01:41:44 -0400108import java.util.HashMap;
Christopher Tate487529a2009-04-29 14:03:25 -0700109import java.util.HashSet;
110import java.util.List;
Christopher Tate91717492009-06-26 21:07:13 -0700111import java.util.Map;
Dan Egnorc1c49c02009-10-30 17:35:39 -0700112import java.util.Random;
Christopher Tateb49ceb32010-02-08 16:22:24 -0800113import java.util.Set;
Christopher Tate4a627c72011-04-01 14:43:32 -0700114import java.util.concurrent.atomic.AtomicBoolean;
Christopher Tate7926a692011-07-11 11:31:57 -0700115import java.util.zip.Deflater;
116import java.util.zip.DeflaterOutputStream;
Christopher Tate7926a692011-07-11 11:31:57 -0700117import java.util.zip.InflaterInputStream;
Christopher Tate487529a2009-04-29 14:03:25 -0700118
Christopher Tate2efd2db2011-07-19 16:32:49 -0700119import javax.crypto.BadPaddingException;
120import javax.crypto.Cipher;
121import javax.crypto.CipherInputStream;
122import javax.crypto.CipherOutputStream;
123import javax.crypto.IllegalBlockSizeException;
124import javax.crypto.NoSuchPaddingException;
125import javax.crypto.SecretKey;
126import javax.crypto.SecretKeyFactory;
127import javax.crypto.spec.IvParameterSpec;
128import javax.crypto.spec.PBEKeySpec;
129import javax.crypto.spec.SecretKeySpec;
130
Christopher Tate487529a2009-04-29 14:03:25 -0700131class BackupManagerService extends IBackupManager.Stub {
132 private static final String TAG = "BackupManagerService";
Christopher Tate4a627c72011-04-01 14:43:32 -0700133 private static final boolean DEBUG = true;
Christopher Tateb1543a92011-09-07 12:11:09 -0700134 private static final boolean MORE_DEBUG = false;
Christopher Tate4a627c72011-04-01 14:43:32 -0700135
136 // Name and current contents version of the full-backup manifest file
137 static final String BACKUP_MANIFEST_FILENAME = "_manifest";
138 static final int BACKUP_MANIFEST_VERSION = 1;
Christopher Tate7bdb0962011-07-13 19:30:21 -0700139 static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
140 static final int BACKUP_FILE_VERSION = 1;
Christopher Tate2efd2db2011-07-19 16:32:49 -0700141 static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
Christopher Tateaa088442009-06-16 18:25:46 -0700142
Christopher Tate73d73692012-01-20 17:11:31 -0800143 static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
144
Christopher Tate49401dd2009-07-01 12:34:29 -0700145 // How often we perform a backup pass. Privileged external callers can
146 // trigger an immediate pass.
Christopher Tateb6787f22009-07-02 17:40:45 -0700147 private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
Christopher Tate487529a2009-04-29 14:03:25 -0700148
Dan Egnorc1c49c02009-10-30 17:35:39 -0700149 // Random variation in backup scheduling time to avoid server load spikes
150 private static final int FUZZ_MILLIS = 5 * 60 * 1000;
151
Christopher Tate8031a3d2009-07-06 16:36:05 -0700152 // The amount of time between the initial provisioning of the device and
153 // the first backup pass.
154 private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
155
Christopher Tate45281862010-03-05 15:46:30 -0800156 private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
157 private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
158 private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
Christopher Tate487529a2009-04-29 14:03:25 -0700159 private static final int MSG_RUN_BACKUP = 1;
Christopher Tate043dadc2009-06-02 16:11:00 -0700160 private static final int MSG_RUN_FULL_BACKUP = 2;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700161 private static final int MSG_RUN_RESTORE = 3;
Christopher Tateee0e78a2009-07-02 11:17:03 -0700162 private static final int MSG_RUN_CLEAR = 4;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700163 private static final int MSG_RUN_INITIALIZE = 5;
Christopher Tate2d449afe2010-03-29 19:14:24 -0700164 private static final int MSG_RUN_GET_RESTORE_SETS = 6;
165 private static final int MSG_TIMEOUT = 7;
Christopher Tate73a3cb32010-12-13 18:27:26 -0800166 private static final int MSG_RESTORE_TIMEOUT = 8;
Christopher Tate4a627c72011-04-01 14:43:32 -0700167 private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
168 private static final int MSG_RUN_FULL_RESTORE = 10;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700169
Christopher Tate8e294d42011-08-31 20:37:12 -0700170 // backup task state machine tick
171 static final int MSG_BACKUP_RESTORE_STEP = 20;
172 static final int MSG_OP_COMPLETE = 21;
173
Christopher Tatec7b31e32009-06-10 15:49:30 -0700174 // Timeout interval for deciding that a bind or clear-data has taken too long
175 static final long TIMEOUT_INTERVAL = 10 * 1000;
176
Christopher Tate44a27902010-01-27 17:15:49 -0800177 // Timeout intervals for agent backup & restore operations
178 static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
Christopher Tate4a627c72011-04-01 14:43:32 -0700179 static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
Christopher Tateb0628bf2011-06-02 15:08:13 -0700180 static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
Christopher Tate44a27902010-01-27 17:15:49 -0800181 static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
182
Christopher Tate2efd2db2011-07-19 16:32:49 -0700183 // User confirmation timeout for a full backup/restore operation. It's this long in
184 // order to give them time to enter the backup password.
185 static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
Christopher Tate4a627c72011-04-01 14:43:32 -0700186
Christopher Tate487529a2009-04-29 14:03:25 -0700187 private Context mContext;
188 private PackageManager mPackageManager;
Christopher Tate1bb69062010-02-19 17:02:12 -0800189 IPackageManager mPackageManagerBinder;
Christopher Tate6ef58a12009-06-29 14:56:28 -0700190 private IActivityManager mActivityManager;
Christopher Tateb6787f22009-07-02 17:40:45 -0700191 private PowerManager mPowerManager;
192 private AlarmManager mAlarmManager;
Christopher Tate32418be2011-10-10 13:51:12 -0700193 private IMountService mMountService;
Christopher Tate44a27902010-01-27 17:15:49 -0800194 IBackupManager mBackupManagerBinder;
Christopher Tateb6787f22009-07-02 17:40:45 -0700195
Christopher Tate73e02522009-07-15 14:18:26 -0700196 boolean mEnabled; // access to this is synchronized on 'this'
197 boolean mProvisioned;
Christopher Tatecce9da52010-02-03 15:11:15 -0800198 boolean mAutoRestore;
Christopher Tate73e02522009-07-15 14:18:26 -0700199 PowerManager.WakeLock mWakelock;
Christopher Tate0bacfd22012-01-11 14:41:19 -0800200 HandlerThread mHandlerThread;
Christopher Tate44a27902010-01-27 17:15:49 -0800201 BackupHandler mBackupHandler;
Christopher Tate4cc86e12009-09-21 19:36:51 -0700202 PendingIntent mRunBackupIntent, mRunInitIntent;
203 BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
Christopher Tatea3d55342012-03-27 13:16:18 -0700204 // map UIDs to the set of participating packages under that UID
205 final SparseArray<HashSet<String>> mBackupParticipants
206 = new SparseArray<HashSet<String>>();
Christopher Tate487529a2009-04-29 14:03:25 -0700207 // set of backup services that have pending changes
Christopher Tate73e02522009-07-15 14:18:26 -0700208 class BackupRequest {
Christopher Tatecc55f812011-08-16 16:06:53 -0700209 public String packageName;
Christopher Tateaa088442009-06-16 18:25:46 -0700210
Christopher Tatecc55f812011-08-16 16:06:53 -0700211 BackupRequest(String pkgName) {
212 packageName = pkgName;
Christopher Tate46758122009-05-06 11:22:00 -0700213 }
Christopher Tate181fafa2009-05-14 11:12:14 -0700214
215 public String toString() {
Christopher Tatecc55f812011-08-16 16:06:53 -0700216 return "BackupRequest{pkg=" + packageName + "}";
Christopher Tate181fafa2009-05-14 11:12:14 -0700217 }
Christopher Tate46758122009-05-06 11:22:00 -0700218 }
Christopher Tatec28083a2010-12-14 16:16:44 -0800219 // Backups that we haven't started yet. Keys are package names.
220 HashMap<String,BackupRequest> mPendingBackups
221 = new HashMap<String,BackupRequest>();
Christopher Tate5cb400b2009-06-25 16:03:14 -0700222
223 // Pseudoname that we use for the Package Manager metadata "package"
Christopher Tate73e02522009-07-15 14:18:26 -0700224 static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
Christopher Tate6aa41f42009-06-19 14:14:22 -0700225
226 // locking around the pending-backup management
Christopher Tate73e02522009-07-15 14:18:26 -0700227 final Object mQueueLock = new Object();
Christopher Tate487529a2009-04-29 14:03:25 -0700228
Christopher Tate043dadc2009-06-02 16:11:00 -0700229 // The thread performing the sequence of queued backups binds to each app's agent
230 // in succession. Bind notifications are asynchronously delivered through the
231 // Activity Manager; use this lock object to signal when a requested binding has
232 // completed.
Christopher Tate73e02522009-07-15 14:18:26 -0700233 final Object mAgentConnectLock = new Object();
234 IBackupAgent mConnectedAgent;
Christopher Tate336a6492011-10-05 16:05:43 -0700235 volatile boolean mBackupRunning;
Christopher Tate73e02522009-07-15 14:18:26 -0700236 volatile boolean mConnecting;
Christopher Tate55f931a2009-09-29 17:17:34 -0700237 volatile long mLastBackupPass;
238 volatile long mNextBackupPass;
Christopher Tate043dadc2009-06-02 16:11:00 -0700239
Christopher Tate6de74ff2012-01-17 15:20:32 -0800240 // For debugging, we maintain a progress trace of operations during backup
241 static final boolean DEBUG_BACKUP_TRACE = true;
242 final List<String> mBackupTrace = new ArrayList<String>();
243
Christopher Tate55f931a2009-09-29 17:17:34 -0700244 // A similar synchronization mechanism around clearing apps' data for restore
Christopher Tate73e02522009-07-15 14:18:26 -0700245 final Object mClearDataLock = new Object();
246 volatile boolean mClearingData;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700247
Christopher Tate91717492009-06-26 21:07:13 -0700248 // Transport bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700249 final HashMap<String,IBackupTransport> mTransports
Christopher Tate91717492009-06-26 21:07:13 -0700250 = new HashMap<String,IBackupTransport>();
Christopher Tate73e02522009-07-15 14:18:26 -0700251 String mCurrentTransport;
252 IBackupTransport mLocalTransport, mGoogleTransport;
Christopher Tate80202c82010-01-25 19:37:47 -0800253 ActiveRestoreSession mActiveRestoreSession;
Christopher Tate043dadc2009-06-02 16:11:00 -0700254
Christopher Tate2d449afe2010-03-29 19:14:24 -0700255 class RestoreGetSetsParams {
256 public IBackupTransport transport;
257 public ActiveRestoreSession session;
258 public IRestoreObserver observer;
259
260 RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
261 IRestoreObserver _observer) {
262 transport = _transport;
263 session = _session;
264 observer = _observer;
265 }
266 }
267
Christopher Tate73e02522009-07-15 14:18:26 -0700268 class RestoreParams {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700269 public IBackupTransport transport;
270 public IRestoreObserver observer;
Dan Egnor156411d2009-06-26 13:20:02 -0700271 public long token;
Christopher Tate84725812010-02-04 15:52:40 -0800272 public PackageInfo pkgInfo;
Christopher Tate1bb69062010-02-19 17:02:12 -0800273 public int pmToken; // in post-install restore, the PM's token for this transaction
Chris Tate249345b2010-10-29 12:57:04 -0700274 public boolean needFullBackup;
Christopher Tate284f1bb2011-07-07 14:31:18 -0700275 public String[] filterSet;
Christopher Tate84725812010-02-04 15:52:40 -0800276
277 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
Chris Tate249345b2010-10-29 12:57:04 -0700278 long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
Christopher Tate84725812010-02-04 15:52:40 -0800279 transport = _transport;
280 observer = _obs;
281 token = _token;
282 pkgInfo = _pkg;
Christopher Tate1bb69062010-02-19 17:02:12 -0800283 pmToken = _pmToken;
Chris Tate249345b2010-10-29 12:57:04 -0700284 needFullBackup = _needFullBackup;
Christopher Tate284f1bb2011-07-07 14:31:18 -0700285 filterSet = null;
Christopher Tate84725812010-02-04 15:52:40 -0800286 }
Christopher Tate7d562ec2009-06-25 18:03:43 -0700287
Chris Tate249345b2010-10-29 12:57:04 -0700288 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
289 boolean _needFullBackup) {
Christopher Tate7d562ec2009-06-25 18:03:43 -0700290 transport = _transport;
291 observer = _obs;
Dan Egnor156411d2009-06-26 13:20:02 -0700292 token = _token;
Christopher Tate84725812010-02-04 15:52:40 -0800293 pkgInfo = null;
Christopher Tate1bb69062010-02-19 17:02:12 -0800294 pmToken = 0;
Chris Tate249345b2010-10-29 12:57:04 -0700295 needFullBackup = _needFullBackup;
Christopher Tate284f1bb2011-07-07 14:31:18 -0700296 filterSet = null;
297 }
298
299 RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token,
300 String[] _filterSet, boolean _needFullBackup) {
301 transport = _transport;
302 observer = _obs;
303 token = _token;
304 pkgInfo = null;
305 pmToken = 0;
306 needFullBackup = _needFullBackup;
307 filterSet = _filterSet;
Christopher Tate7d562ec2009-06-25 18:03:43 -0700308 }
309 }
310
Christopher Tate73e02522009-07-15 14:18:26 -0700311 class ClearParams {
Christopher Tateee0e78a2009-07-02 11:17:03 -0700312 public IBackupTransport transport;
313 public PackageInfo packageInfo;
314
315 ClearParams(IBackupTransport _transport, PackageInfo _info) {
316 transport = _transport;
317 packageInfo = _info;
318 }
319 }
320
Christopher Tate4a627c72011-04-01 14:43:32 -0700321 class FullParams {
322 public ParcelFileDescriptor fd;
323 public final AtomicBoolean latch;
324 public IFullBackupRestoreObserver observer;
Christopher Tate728a1c42011-07-28 18:03:03 -0700325 public String curPassword; // filled in by the confirmation step
326 public String encryptPassword;
Christopher Tate4a627c72011-04-01 14:43:32 -0700327
328 FullParams() {
329 latch = new AtomicBoolean(false);
330 }
331 }
332
333 class FullBackupParams extends FullParams {
334 public boolean includeApks;
335 public boolean includeShared;
336 public boolean allApps;
Christopher Tate240c7d22011-10-03 18:13:44 -0700337 public boolean includeSystem;
Christopher Tate4a627c72011-04-01 14:43:32 -0700338 public String[] packages;
339
340 FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
Christopher Tate240c7d22011-10-03 18:13:44 -0700341 boolean doAllApps, boolean doSystem, String[] pkgList) {
Christopher Tate4a627c72011-04-01 14:43:32 -0700342 fd = output;
343 includeApks = saveApks;
344 includeShared = saveShared;
345 allApps = doAllApps;
Christopher Tate240c7d22011-10-03 18:13:44 -0700346 includeSystem = doSystem;
Christopher Tate4a627c72011-04-01 14:43:32 -0700347 packages = pkgList;
348 }
349 }
350
351 class FullRestoreParams extends FullParams {
352 FullRestoreParams(ParcelFileDescriptor input) {
353 fd = input;
354 }
355 }
356
Christopher Tate44a27902010-01-27 17:15:49 -0800357 // Bookkeeping of in-flight operations for timeout etc. purposes. The operation
358 // token is the index of the entry in the pending-operations list.
359 static final int OP_PENDING = 0;
360 static final int OP_ACKNOWLEDGED = 1;
361 static final int OP_TIMEOUT = -1;
362
Christopher Tate8e294d42011-08-31 20:37:12 -0700363 class Operation {
364 public int state;
365 public BackupRestoreTask callback;
366
367 Operation(int initialState, BackupRestoreTask callbackObj) {
368 state = initialState;
369 callback = callbackObj;
370 }
371 }
372 final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
Christopher Tate44a27902010-01-27 17:15:49 -0800373 final Object mCurrentOpLock = new Object();
374 final Random mTokenGenerator = new Random();
375
Christopher Tate4a627c72011-04-01 14:43:32 -0700376 final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
377
Christopher Tate5cb400b2009-06-25 16:03:14 -0700378 // Where we keep our journal files and other bookkeeping
Christopher Tate73e02522009-07-15 14:18:26 -0700379 File mBaseStateDir;
380 File mDataDir;
381 File mJournalDir;
382 File mJournal;
Christopher Tate73e02522009-07-15 14:18:26 -0700383
Christopher Tate2efd2db2011-07-19 16:32:49 -0700384 // Backup password, if any, and the file where it's saved. What is stored is not the
385 // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but
386 // persisted) salt. Validation is performed by running the challenge text through the
387 // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches
388 // the saved hash string, then the challenge text matches the originally supplied
389 // password text.
390 private final SecureRandom mRng = new SecureRandom();
391 private String mPasswordHash;
392 private File mPasswordHashFile;
393 private byte[] mPasswordSalt;
394
395 // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys
396 static final int PBKDF2_HASH_ROUNDS = 10000;
397 static final int PBKDF2_KEY_SIZE = 256; // bits
398 static final int PBKDF2_SALT_SIZE = 512; // bits
399 static final String ENCRYPTION_ALGORITHM_NAME = "AES-256";
400
Christopher Tate84725812010-02-04 15:52:40 -0800401 // Keep a log of all the apps we've ever backed up, and what the
402 // dataset tokens are for both the current backup dataset and
403 // the ancestral dataset.
Christopher Tate73e02522009-07-15 14:18:26 -0700404 private File mEverStored;
Christopher Tate73e02522009-07-15 14:18:26 -0700405 HashSet<String> mEverStoredApps = new HashSet<String>();
406
Christopher Tateb49ceb32010-02-08 16:22:24 -0800407 static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1; // increment when the schema changes
Christopher Tate84725812010-02-04 15:52:40 -0800408 File mTokenFile;
Christopher Tateb49ceb32010-02-08 16:22:24 -0800409 Set<String> mAncestralPackages = null;
Christopher Tate84725812010-02-04 15:52:40 -0800410 long mAncestralToken = 0;
411 long mCurrentToken = 0;
412
Christopher Tate4cc86e12009-09-21 19:36:51 -0700413 // Persistently track the need to do a full init
414 static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
415 HashSet<String> mPendingInits = new HashSet<String>(); // transport names
Christopher Tateaa088442009-06-16 18:25:46 -0700416
Christopher Tate4a627c72011-04-01 14:43:32 -0700417 // Utility: build a new random integer token
418 int generateToken() {
419 int token;
420 do {
421 synchronized (mTokenGenerator) {
422 token = mTokenGenerator.nextInt();
423 }
424 } while (token < 0);
425 return token;
426 }
427
Christopher Tate44a27902010-01-27 17:15:49 -0800428 // ----- Asynchronous backup/restore handler thread -----
429
430 private class BackupHandler extends Handler {
431 public BackupHandler(Looper looper) {
432 super(looper);
433 }
434
435 public void handleMessage(Message msg) {
436
437 switch (msg.what) {
438 case MSG_RUN_BACKUP:
439 {
440 mLastBackupPass = System.currentTimeMillis();
441 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
442
443 IBackupTransport transport = getTransport(mCurrentTransport);
444 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800445 Slog.v(TAG, "Backup requested but no transport available");
Christopher Tate336a6492011-10-05 16:05:43 -0700446 synchronized (mQueueLock) {
447 mBackupRunning = false;
448 }
Christopher Tate44a27902010-01-27 17:15:49 -0800449 mWakelock.release();
450 break;
451 }
452
453 // snapshot the pending-backup set and work on that
454 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
Christopher Tatec61da312010-02-05 10:41:27 -0800455 File oldJournal = mJournal;
Christopher Tate44a27902010-01-27 17:15:49 -0800456 synchronized (mQueueLock) {
Christopher Tatec61da312010-02-05 10:41:27 -0800457 // Do we have any work to do? Construct the work queue
458 // then release the synchronization lock to actually run
459 // the backup.
Christopher Tate44a27902010-01-27 17:15:49 -0800460 if (mPendingBackups.size() > 0) {
461 for (BackupRequest b: mPendingBackups.values()) {
462 queue.add(b);
463 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800464 if (DEBUG) Slog.v(TAG, "clearing pending backups");
Christopher Tate44a27902010-01-27 17:15:49 -0800465 mPendingBackups.clear();
466
467 // Start a new backup-queue journal file too
Christopher Tate44a27902010-01-27 17:15:49 -0800468 mJournal = null;
469
Christopher Tate44a27902010-01-27 17:15:49 -0800470 }
471 }
Christopher Tatec61da312010-02-05 10:41:27 -0800472
Christopher Tate8e294d42011-08-31 20:37:12 -0700473 // At this point, we have started a new journal file, and the old
474 // file identity is being passed to the backup processing task.
475 // When it completes successfully, that old journal file will be
476 // deleted. If we crash prior to that, the old journal is parsed
477 // at next boot and the journaled requests fulfilled.
Christopher Tatec61da312010-02-05 10:41:27 -0800478 if (queue.size() > 0) {
Christopher Tate8e294d42011-08-31 20:37:12 -0700479 // Spin up a backup state sequence and set it running
480 PerformBackupTask pbt = new PerformBackupTask(transport, queue, oldJournal);
481 Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
482 sendMessage(pbtMessage);
Christopher Tatec61da312010-02-05 10:41:27 -0800483 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800484 Slog.v(TAG, "Backup requested but nothing pending");
Christopher Tate336a6492011-10-05 16:05:43 -0700485 synchronized (mQueueLock) {
486 mBackupRunning = false;
487 }
Christopher Tatec61da312010-02-05 10:41:27 -0800488 mWakelock.release();
489 }
Christopher Tate44a27902010-01-27 17:15:49 -0800490 break;
491 }
492
Christopher Tate8e294d42011-08-31 20:37:12 -0700493 case MSG_BACKUP_RESTORE_STEP:
494 {
495 try {
496 BackupRestoreTask task = (BackupRestoreTask) msg.obj;
497 if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing");
498 task.execute();
499 } catch (ClassCastException e) {
500 Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
501 }
502 break;
503 }
504
505 case MSG_OP_COMPLETE:
506 {
507 try {
Christopher Tate2982d062011-09-06 20:35:24 -0700508 BackupRestoreTask task = (BackupRestoreTask) msg.obj;
509 task.operationComplete();
Christopher Tate8e294d42011-08-31 20:37:12 -0700510 } catch (ClassCastException e) {
511 Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
512 }
513 break;
514 }
515
Christopher Tate44a27902010-01-27 17:15:49 -0800516 case MSG_RUN_FULL_BACKUP:
Christopher Tate4a627c72011-04-01 14:43:32 -0700517 {
Christopher Tatea28e8542011-09-12 13:45:21 -0700518 // TODO: refactor full backup to be a looper-based state machine
519 // similar to normal backup/restore.
Christopher Tate4a627c72011-04-01 14:43:32 -0700520 FullBackupParams params = (FullBackupParams)msg.obj;
Christopher Tatea28e8542011-09-12 13:45:21 -0700521 PerformFullBackupTask task = new PerformFullBackupTask(params.fd,
522 params.observer, params.includeApks,
Christopher Tate728a1c42011-07-28 18:03:03 -0700523 params.includeShared, params.curPassword, params.encryptPassword,
Christopher Tate240c7d22011-10-03 18:13:44 -0700524 params.allApps, params.includeSystem, params.packages, params.latch);
Christopher Tatea28e8542011-09-12 13:45:21 -0700525 (new Thread(task)).start();
Christopher Tate44a27902010-01-27 17:15:49 -0800526 break;
Christopher Tate4a627c72011-04-01 14:43:32 -0700527 }
Christopher Tate44a27902010-01-27 17:15:49 -0800528
529 case MSG_RUN_RESTORE:
530 {
531 RestoreParams params = (RestoreParams)msg.obj;
Joe Onorato8a9b2202010-02-26 18:56:32 -0800532 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
Christopher Tate2982d062011-09-06 20:35:24 -0700533 PerformRestoreTask task = new PerformRestoreTask(
534 params.transport, params.observer,
Chris Tate249345b2010-10-29 12:57:04 -0700535 params.token, params.pkgInfo, params.pmToken,
Christopher Tate2982d062011-09-06 20:35:24 -0700536 params.needFullBackup, params.filterSet);
537 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
538 sendMessage(restoreMsg);
Christopher Tate44a27902010-01-27 17:15:49 -0800539 break;
540 }
541
Christopher Tate75a99702011-05-18 16:28:19 -0700542 case MSG_RUN_FULL_RESTORE:
543 {
Christopher Tatea28e8542011-09-12 13:45:21 -0700544 // TODO: refactor full restore to be a looper-based state machine
545 // similar to normal backup/restore.
Christopher Tate75a99702011-05-18 16:28:19 -0700546 FullRestoreParams params = (FullRestoreParams)msg.obj;
Christopher Tatea28e8542011-09-12 13:45:21 -0700547 PerformFullRestoreTask task = new PerformFullRestoreTask(params.fd,
548 params.curPassword, params.encryptPassword,
549 params.observer, params.latch);
550 (new Thread(task)).start();
Christopher Tate75a99702011-05-18 16:28:19 -0700551 break;
552 }
553
Christopher Tate44a27902010-01-27 17:15:49 -0800554 case MSG_RUN_CLEAR:
555 {
556 ClearParams params = (ClearParams)msg.obj;
557 (new PerformClearTask(params.transport, params.packageInfo)).run();
558 break;
559 }
560
561 case MSG_RUN_INITIALIZE:
562 {
563 HashSet<String> queue;
564
565 // Snapshot the pending-init queue and work on that
566 synchronized (mQueueLock) {
567 queue = new HashSet<String>(mPendingInits);
568 mPendingInits.clear();
569 }
570
571 (new PerformInitializeTask(queue)).run();
572 break;
573 }
574
Christopher Tate2d449afe2010-03-29 19:14:24 -0700575 case MSG_RUN_GET_RESTORE_SETS:
576 {
577 // Like other async operations, this is entered with the wakelock held
578 RestoreSet[] sets = null;
579 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
580 try {
581 sets = params.transport.getAvailableRestoreSets();
582 // cache the result in the active session
583 synchronized (params.session) {
584 params.session.mRestoreSets = sets;
585 }
586 if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
587 } catch (Exception e) {
588 Slog.e(TAG, "Error from transport getting set list");
589 } finally {
590 if (params.observer != null) {
591 try {
592 params.observer.restoreSetsAvailable(sets);
593 } catch (RemoteException re) {
594 Slog.e(TAG, "Unable to report listing to observer");
595 } catch (Exception e) {
596 Slog.e(TAG, "Restore observer threw", e);
597 }
598 }
599
Christopher Tate2a935092011-03-03 17:30:32 -0800600 // Done: reset the session timeout clock
601 removeMessages(MSG_RESTORE_TIMEOUT);
602 sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
603
Christopher Tate2d449afe2010-03-29 19:14:24 -0700604 mWakelock.release();
605 }
606 break;
607 }
608
Christopher Tate44a27902010-01-27 17:15:49 -0800609 case MSG_TIMEOUT:
610 {
Christopher Tate8e294d42011-08-31 20:37:12 -0700611 handleTimeout(msg.arg1, msg.obj);
Christopher Tate44a27902010-01-27 17:15:49 -0800612 break;
613 }
Christopher Tate73a3cb32010-12-13 18:27:26 -0800614
615 case MSG_RESTORE_TIMEOUT:
616 {
617 synchronized (BackupManagerService.this) {
618 if (mActiveRestoreSession != null) {
619 // Client app left the restore session dangling. We know that it
620 // can't be in the middle of an actual restore operation because
Christopher Tate2982d062011-09-06 20:35:24 -0700621 // the timeout is suspended while a restore is in progress. Clean
Christopher Tate73a3cb32010-12-13 18:27:26 -0800622 // up now.
623 Slog.w(TAG, "Restore session timed out; aborting");
624 post(mActiveRestoreSession.new EndRestoreRunnable(
625 BackupManagerService.this, mActiveRestoreSession));
626 }
627 }
628 }
Christopher Tate4a627c72011-04-01 14:43:32 -0700629
630 case MSG_FULL_CONFIRMATION_TIMEOUT:
631 {
632 synchronized (mFullConfirmations) {
633 FullParams params = mFullConfirmations.get(msg.arg1);
634 if (params != null) {
635 Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
636
637 // Release the waiter; timeout == completion
638 signalFullBackupRestoreCompletion(params);
639
640 // Remove the token from the set
641 mFullConfirmations.delete(msg.arg1);
642
643 // Report a timeout to the observer, if any
644 if (params.observer != null) {
645 try {
646 params.observer.onTimeout();
647 } catch (RemoteException e) {
648 /* don't care if the app has gone away */
649 }
650 }
651 } else {
652 Slog.d(TAG, "couldn't find params for token " + msg.arg1);
653 }
654 }
655 break;
656 }
Christopher Tate44a27902010-01-27 17:15:49 -0800657 }
658 }
659 }
660
Christopher Tate6de74ff2012-01-17 15:20:32 -0800661 // ----- Debug-only backup operation trace -----
662 void addBackupTrace(String s) {
663 if (DEBUG_BACKUP_TRACE) {
664 synchronized (mBackupTrace) {
665 mBackupTrace.add(s);
666 }
667 }
668 }
669
670 void clearBackupTrace() {
671 if (DEBUG_BACKUP_TRACE) {
672 synchronized (mBackupTrace) {
673 mBackupTrace.clear();
674 }
675 }
676 }
677
Christopher Tate44a27902010-01-27 17:15:49 -0800678 // ----- Main service implementation -----
679
Christopher Tate487529a2009-04-29 14:03:25 -0700680 public BackupManagerService(Context context) {
681 mContext = context;
682 mPackageManager = context.getPackageManager();
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -0700683 mPackageManagerBinder = AppGlobals.getPackageManager();
Christopher Tate181fafa2009-05-14 11:12:14 -0700684 mActivityManager = ActivityManagerNative.getDefault();
Christopher Tate487529a2009-04-29 14:03:25 -0700685
Christopher Tateb6787f22009-07-02 17:40:45 -0700686 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
687 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
Christopher Tate32418be2011-10-10 13:51:12 -0700688 mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
Christopher Tateb6787f22009-07-02 17:40:45 -0700689
Christopher Tate44a27902010-01-27 17:15:49 -0800690 mBackupManagerBinder = asInterface(asBinder());
691
692 // spin up the backup/restore handler thread
693 mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
694 mHandlerThread.start();
695 mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
696
Christopher Tate22b87872009-05-04 16:41:53 -0700697 // Set up our bookkeeping
Christopher Tateb6787f22009-07-02 17:40:45 -0700698 boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
Dianne Hackborncf098292009-07-01 19:55:20 -0700699 Settings.Secure.BACKUP_ENABLED, 0) != 0;
Christopher Tate8031a3d2009-07-06 16:36:05 -0700700 mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
Joe Onoratoab9a2a52009-07-27 08:56:39 -0700701 Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
Christopher Tatecce9da52010-02-03 15:11:15 -0800702 mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
Christopher Tate5035fda2010-02-25 18:01:14 -0800703 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
Oscar Montemayora8529f62009-11-18 10:14:20 -0800704 // If Encrypted file systems is enabled or disabled, this call will return the
705 // correct directory.
Jason parksa3cdaa52011-01-13 14:15:43 -0600706 mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
Oscar Montemayora8529f62009-11-18 10:14:20 -0800707 mBaseStateDir.mkdirs();
Christopher Tatef4172472009-05-05 15:50:03 -0700708 mDataDir = Environment.getDownloadCacheDirectory();
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700709
Christopher Tate2efd2db2011-07-19 16:32:49 -0700710 mPasswordHashFile = new File(mBaseStateDir, "pwhash");
711 if (mPasswordHashFile.exists()) {
712 FileInputStream fin = null;
713 DataInputStream in = null;
714 try {
715 fin = new FileInputStream(mPasswordHashFile);
716 in = new DataInputStream(new BufferedInputStream(fin));
717 // integer length of the salt array, followed by the salt,
718 // then the hex pw hash string
719 int saltLen = in.readInt();
720 byte[] salt = new byte[saltLen];
721 in.readFully(salt);
722 mPasswordHash = in.readUTF();
723 mPasswordSalt = salt;
724 } catch (IOException e) {
725 Slog.e(TAG, "Unable to read saved backup pw hash");
726 } finally {
727 try {
728 if (in != null) in.close();
729 if (fin != null) fin.close();
730 } catch (IOException e) {
731 Slog.w(TAG, "Unable to close streams");
732 }
733 }
734 }
735
Christopher Tate4cc86e12009-09-21 19:36:51 -0700736 // Alarm receivers for scheduled backups & initialization operations
Christopher Tateb6787f22009-07-02 17:40:45 -0700737 mRunBackupReceiver = new RunBackupReceiver();
Christopher Tate4cc86e12009-09-21 19:36:51 -0700738 IntentFilter filter = new IntentFilter();
739 filter.addAction(RUN_BACKUP_ACTION);
740 context.registerReceiver(mRunBackupReceiver, filter,
741 android.Manifest.permission.BACKUP, null);
742
743 mRunInitReceiver = new RunInitializeReceiver();
744 filter = new IntentFilter();
745 filter.addAction(RUN_INITIALIZE_ACTION);
746 context.registerReceiver(mRunInitReceiver, filter,
747 android.Manifest.permission.BACKUP, null);
Christopher Tateb6787f22009-07-02 17:40:45 -0700748
749 Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
Christopher Tateb6787f22009-07-02 17:40:45 -0700750 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
751 mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
752
Christopher Tate4cc86e12009-09-21 19:36:51 -0700753 Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
754 backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
755 mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
756
Christopher Tatecde87f42009-06-12 12:55:53 -0700757 // Set up the backup-request journaling
Christopher Tate5cb400b2009-06-25 16:03:14 -0700758 mJournalDir = new File(mBaseStateDir, "pending");
759 mJournalDir.mkdirs(); // creates mBaseStateDir along the way
Dan Egnor852f8e42009-09-30 11:20:45 -0700760 mJournal = null; // will be created on first use
Christopher Tatecde87f42009-06-12 12:55:53 -0700761
Christopher Tate73e02522009-07-15 14:18:26 -0700762 // Set up the various sorts of package tracking we do
763 initPackageTracking();
764
Christopher Tateabce4e82009-06-18 18:35:32 -0700765 // Build our mapping of uid to backup client services. This implicitly
766 // schedules a backup pass on the Package Manager metadata the first
767 // time anything needs to be backed up.
Christopher Tate3799bc22009-05-06 16:13:56 -0700768 synchronized (mBackupParticipants) {
769 addPackageParticipantsLocked(null);
Christopher Tate487529a2009-04-29 14:03:25 -0700770 }
771
Dan Egnor87a02bc2009-06-17 02:30:10 -0700772 // Set up our transport options and initialize the default transport
773 // TODO: Have transports register themselves somehow?
774 // TODO: Don't create transports that we don't need to?
Dan Egnor87a02bc2009-06-17 02:30:10 -0700775 mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
Christopher Tate91717492009-06-26 21:07:13 -0700776 ComponentName localName = new ComponentName(context, LocalTransport.class);
777 registerTransport(localName.flattenToShortString(), mLocalTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700778
Christopher Tate91717492009-06-26 21:07:13 -0700779 mGoogleTransport = null;
Dianne Hackborncf098292009-07-01 19:55:20 -0700780 mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
781 Settings.Secure.BACKUP_TRANSPORT);
782 if ("".equals(mCurrentTransport)) {
783 mCurrentTransport = null;
Christopher Tatece0bf062009-07-01 11:43:53 -0700784 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800785 if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -0700786
787 // Attach to the Google backup transport. When this comes up, it will set
788 // itself as the current transport because we explicitly reset mCurrentTransport
789 // to null.
Christopher Tatea32504f2010-04-21 17:58:07 -0700790 ComponentName transportComponent = new ComponentName("com.google.android.backup",
791 "com.google.android.backup.BackupTransportService");
792 try {
793 // If there's something out there that is supposed to be the Google
794 // backup transport, make sure it's legitimately part of the OS build
795 // and not an app lying about its package name.
796 ApplicationInfo info = mPackageManager.getApplicationInfo(
797 transportComponent.getPackageName(), 0);
798 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
799 if (DEBUG) Slog.v(TAG, "Binding to Google transport");
800 Intent intent = new Intent().setComponent(transportComponent);
801 context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
802 } else {
803 Slog.w(TAG, "Possible Google transport spoof: ignoring " + info);
804 }
805 } catch (PackageManager.NameNotFoundException nnf) {
806 // No such package? No binding.
807 if (DEBUG) Slog.v(TAG, "Google transport not present");
808 }
Christopher Tateaa088442009-06-16 18:25:46 -0700809
Christopher Tatecde87f42009-06-12 12:55:53 -0700810 // Now that we know about valid backup participants, parse any
Christopher Tate49401dd2009-07-01 12:34:29 -0700811 // leftover journal files into the pending backup set
Christopher Tatecde87f42009-06-12 12:55:53 -0700812 parseLeftoverJournals();
813
Christopher Tateb6787f22009-07-02 17:40:45 -0700814 // Power management
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700815 mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
Christopher Tateb6787f22009-07-02 17:40:45 -0700816
817 // Start the backup passes going
818 setBackupEnabled(areEnabled);
819 }
820
821 private class RunBackupReceiver extends BroadcastReceiver {
822 public void onReceive(Context context, Intent intent) {
823 if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
Christopher Tateb6787f22009-07-02 17:40:45 -0700824 synchronized (mQueueLock) {
Christopher Tate4cc86e12009-09-21 19:36:51 -0700825 if (mPendingInits.size() > 0) {
826 // If there are pending init operations, we process those
827 // and then settle into the usual periodic backup schedule.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800828 if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700829 try {
830 mAlarmManager.cancel(mRunInitIntent);
831 mRunInitIntent.send();
832 } catch (PendingIntent.CanceledException ce) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800833 Slog.e(TAG, "Run init intent cancelled");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700834 // can't really do more than bail here
835 }
836 } else {
Christopher Tatec2af5d32010-02-02 15:18:58 -0800837 // Don't run backups now if we're disabled or not yet
838 // fully set up.
839 if (mEnabled && mProvisioned) {
Christopher Tate336a6492011-10-05 16:05:43 -0700840 if (!mBackupRunning) {
841 if (DEBUG) Slog.v(TAG, "Running a backup pass");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700842
Christopher Tate336a6492011-10-05 16:05:43 -0700843 // Acquire the wakelock and pass it to the backup thread. it will
844 // be released once backup concludes.
845 mBackupRunning = true;
846 mWakelock.acquire();
Christopher Tate4cc86e12009-09-21 19:36:51 -0700847
Christopher Tate336a6492011-10-05 16:05:43 -0700848 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
849 mBackupHandler.sendMessage(msg);
850 } else {
851 Slog.i(TAG, "Backup time but one already running");
852 }
Christopher Tate4cc86e12009-09-21 19:36:51 -0700853 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800854 Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
Christopher Tate4cc86e12009-09-21 19:36:51 -0700855 }
856 }
857 }
858 }
859 }
860 }
861
862 private class RunInitializeReceiver extends BroadcastReceiver {
863 public void onReceive(Context context, Intent intent) {
864 if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
865 synchronized (mQueueLock) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800866 if (DEBUG) Slog.v(TAG, "Running a device init");
Christopher Tate4cc86e12009-09-21 19:36:51 -0700867
868 // Acquire the wakelock and pass it to the init thread. it will
869 // be released once init concludes.
Christopher Tateb6787f22009-07-02 17:40:45 -0700870 mWakelock.acquire();
871
Christopher Tate4cc86e12009-09-21 19:36:51 -0700872 Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
Christopher Tateb6787f22009-07-02 17:40:45 -0700873 mBackupHandler.sendMessage(msg);
874 }
875 }
Christopher Tate49401dd2009-07-01 12:34:29 -0700876 }
Christopher Tateb6787f22009-07-02 17:40:45 -0700877 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700878
Christopher Tate73e02522009-07-15 14:18:26 -0700879 private void initPackageTracking() {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800880 if (DEBUG) Slog.v(TAG, "Initializing package tracking");
Christopher Tate73e02522009-07-15 14:18:26 -0700881
Christopher Tate84725812010-02-04 15:52:40 -0800882 // Remember our ancestral dataset
883 mTokenFile = new File(mBaseStateDir, "ancestral");
884 try {
885 RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
Christopher Tateb49ceb32010-02-08 16:22:24 -0800886 int version = tf.readInt();
887 if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
888 mAncestralToken = tf.readLong();
889 mCurrentToken = tf.readLong();
890
891 int numPackages = tf.readInt();
892 if (numPackages >= 0) {
893 mAncestralPackages = new HashSet<String>();
894 for (int i = 0; i < numPackages; i++) {
895 String pkgName = tf.readUTF();
896 mAncestralPackages.add(pkgName);
897 }
898 }
899 }
Brad Fitzpatrick725d8f02010-11-15 11:12:42 -0800900 tf.close();
Christopher Tate1168baa2010-02-17 13:03:40 -0800901 } catch (FileNotFoundException fnf) {
902 // Probably innocuous
Joe Onorato8a9b2202010-02-26 18:56:32 -0800903 Slog.v(TAG, "No ancestral data");
Christopher Tate84725812010-02-04 15:52:40 -0800904 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800905 Slog.w(TAG, "Unable to read token file", e);
Christopher Tate84725812010-02-04 15:52:40 -0800906 }
907
Christopher Tatee97e8072009-07-15 16:45:50 -0700908 // Keep a log of what apps we've ever backed up. Because we might have
909 // rebooted in the middle of an operation that was removing something from
910 // this log, we sanity-check its contents here and reconstruct it.
Christopher Tate73e02522009-07-15 14:18:26 -0700911 mEverStored = new File(mBaseStateDir, "processed");
Christopher Tatee97e8072009-07-15 16:45:50 -0700912 File tempProcessedFile = new File(mBaseStateDir, "processed.new");
Christopher Tate73e02522009-07-15 14:18:26 -0700913
Christopher Tatee97e8072009-07-15 16:45:50 -0700914 // If we were in the middle of removing something from the ever-backed-up
915 // file, there might be a transient "processed.new" file still present.
Dan Egnor852f8e42009-09-30 11:20:45 -0700916 // Ignore it -- we'll validate "processed" against the current package set.
Christopher Tatee97e8072009-07-15 16:45:50 -0700917 if (tempProcessedFile.exists()) {
918 tempProcessedFile.delete();
919 }
920
Dan Egnor852f8e42009-09-30 11:20:45 -0700921 // If there are previous contents, parse them out then start a new
922 // file to continue the recordkeeping.
923 if (mEverStored.exists()) {
924 RandomAccessFile temp = null;
925 RandomAccessFile in = null;
926
927 try {
928 temp = new RandomAccessFile(tempProcessedFile, "rws");
929 in = new RandomAccessFile(mEverStored, "r");
930
931 while (true) {
932 PackageInfo info;
933 String pkg = in.readUTF();
934 try {
935 info = mPackageManager.getPackageInfo(pkg, 0);
936 mEverStoredApps.add(pkg);
937 temp.writeUTF(pkg);
Christopher Tatec58efa62011-08-01 19:20:14 -0700938 if (MORE_DEBUG) Slog.v(TAG, " + " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700939 } catch (NameNotFoundException e) {
940 // nope, this package was uninstalled; don't include it
Christopher Tatec58efa62011-08-01 19:20:14 -0700941 if (MORE_DEBUG) Slog.v(TAG, " - " + pkg);
Dan Egnor852f8e42009-09-30 11:20:45 -0700942 }
943 }
944 } catch (EOFException e) {
945 // Once we've rewritten the backup history log, atomically replace the
946 // old one with the new one then reopen the file for continuing use.
947 if (!tempProcessedFile.renameTo(mEverStored)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800948 Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -0700949 }
950 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800951 Slog.e(TAG, "Error in processed file", e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700952 } finally {
953 try { if (temp != null) temp.close(); } catch (IOException e) {}
954 try { if (in != null) in.close(); } catch (IOException e) {}
955 }
956 }
957
Christopher Tate73e02522009-07-15 14:18:26 -0700958 // Register for broadcasts about package install, etc., so we can
959 // update the provider list.
960 IntentFilter filter = new IntentFilter();
961 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
962 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
963 filter.addDataScheme("package");
964 mContext.registerReceiver(mBroadcastReceiver, filter);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800965 // Register for events related to sdcard installation.
966 IntentFilter sdFilter = new IntentFilter();
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800967 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
968 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800969 mContext.registerReceiver(mBroadcastReceiver, sdFilter);
Christopher Tate73e02522009-07-15 14:18:26 -0700970 }
971
Christopher Tatecde87f42009-06-12 12:55:53 -0700972 private void parseLeftoverJournals() {
Dan Egnor852f8e42009-09-30 11:20:45 -0700973 for (File f : mJournalDir.listFiles()) {
974 if (mJournal == null || f.compareTo(mJournal) != 0) {
975 // This isn't the current journal, so it must be a leftover. Read
976 // out the package names mentioned there and schedule them for
977 // backup.
978 RandomAccessFile in = null;
979 try {
Joe Onorato431bb222010-10-18 19:13:23 -0400980 Slog.i(TAG, "Found stale backup journal, scheduling");
Dan Egnor852f8e42009-09-30 11:20:45 -0700981 in = new RandomAccessFile(f, "r");
982 while (true) {
983 String packageName = in.readUTF();
Joe Onorato431bb222010-10-18 19:13:23 -0400984 Slog.i(TAG, " " + packageName);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -0700985 dataChangedImpl(packageName);
Christopher Tatecde87f42009-06-12 12:55:53 -0700986 }
Dan Egnor852f8e42009-09-30 11:20:45 -0700987 } catch (EOFException e) {
988 // no more data; we're done
989 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800990 Slog.e(TAG, "Can't read " + f, e);
Dan Egnor852f8e42009-09-30 11:20:45 -0700991 } finally {
992 // close/delete the file
993 try { if (in != null) in.close(); } catch (IOException e) {}
994 f.delete();
Christopher Tatecde87f42009-06-12 12:55:53 -0700995 }
996 }
997 }
998 }
999
Christopher Tate2efd2db2011-07-19 16:32:49 -07001000 private SecretKey buildPasswordKey(String pw, byte[] salt, int rounds) {
1001 return buildCharArrayKey(pw.toCharArray(), salt, rounds);
1002 }
1003
1004 private SecretKey buildCharArrayKey(char[] pwArray, byte[] salt, int rounds) {
1005 try {
1006 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
1007 KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
1008 return keyFactory.generateSecret(ks);
1009 } catch (InvalidKeySpecException e) {
1010 Slog.e(TAG, "Invalid key spec for PBKDF2!");
1011 } catch (NoSuchAlgorithmException e) {
1012 Slog.e(TAG, "PBKDF2 unavailable!");
1013 }
1014 return null;
1015 }
1016
1017 private String buildPasswordHash(String pw, byte[] salt, int rounds) {
1018 SecretKey key = buildPasswordKey(pw, salt, rounds);
1019 if (key != null) {
1020 return byteArrayToHex(key.getEncoded());
1021 }
1022 return null;
1023 }
1024
1025 private String byteArrayToHex(byte[] data) {
1026 StringBuilder buf = new StringBuilder(data.length * 2);
1027 for (int i = 0; i < data.length; i++) {
1028 buf.append(Byte.toHexString(data[i], true));
1029 }
1030 return buf.toString();
1031 }
1032
1033 private byte[] hexToByteArray(String digits) {
1034 final int bytes = digits.length() / 2;
1035 if (2*bytes != digits.length()) {
1036 throw new IllegalArgumentException("Hex string must have an even number of digits");
1037 }
1038
1039 byte[] result = new byte[bytes];
1040 for (int i = 0; i < digits.length(); i += 2) {
1041 result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16);
1042 }
1043 return result;
1044 }
1045
1046 private byte[] makeKeyChecksum(byte[] pwBytes, byte[] salt, int rounds) {
1047 char[] mkAsChar = new char[pwBytes.length];
1048 for (int i = 0; i < pwBytes.length; i++) {
1049 mkAsChar[i] = (char) pwBytes[i];
1050 }
1051
1052 Key checksum = buildCharArrayKey(mkAsChar, salt, rounds);
1053 return checksum.getEncoded();
1054 }
1055
1056 // Used for generating random salts or passwords
1057 private byte[] randomBytes(int bits) {
1058 byte[] array = new byte[bits / 8];
1059 mRng.nextBytes(array);
1060 return array;
1061 }
1062
1063 // Backup password management
1064 boolean passwordMatchesSaved(String candidatePw, int rounds) {
Christopher Tate32418be2011-10-10 13:51:12 -07001065 // First, on an encrypted device we require matching the device pw
1066 final boolean isEncrypted;
1067 try {
1068 isEncrypted = (mMountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE);
1069 if (isEncrypted) {
1070 if (DEBUG) {
1071 Slog.i(TAG, "Device encrypted; verifying against device data pw");
1072 }
1073 // 0 means the password validated
1074 // -2 means device not encrypted
1075 // Any other result is either password failure or an error condition,
1076 // so we refuse the match
1077 final int result = mMountService.verifyEncryptionPassword(candidatePw);
1078 if (result == 0) {
1079 if (MORE_DEBUG) Slog.d(TAG, "Pw verifies");
1080 return true;
1081 } else if (result != -2) {
1082 if (MORE_DEBUG) Slog.d(TAG, "Pw mismatch");
1083 return false;
1084 } else {
1085 // ...else the device is supposedly not encrypted. HOWEVER, the
1086 // query about the encryption state said that the device *is*
1087 // encrypted, so ... we may have a problem. Log it and refuse
1088 // the backup.
1089 Slog.e(TAG, "verified encryption state mismatch against query; no match allowed");
1090 return false;
1091 }
1092 }
1093 } catch (Exception e) {
1094 // Something went wrong talking to the mount service. This is very bad;
1095 // assume that we fail password validation.
1096 return false;
1097 }
1098
Christopher Tate2efd2db2011-07-19 16:32:49 -07001099 if (mPasswordHash == null) {
1100 // no current password case -- require that 'currentPw' be null or empty
1101 if (candidatePw == null || "".equals(candidatePw)) {
1102 return true;
1103 } // else the non-empty candidate does not match the empty stored pw
1104 } else {
1105 // hash the stated current pw and compare to the stored one
1106 if (candidatePw != null && candidatePw.length() > 0) {
1107 String currentPwHash = buildPasswordHash(candidatePw, mPasswordSalt, rounds);
1108 if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
1109 // candidate hash matches the stored hash -- the password matches
1110 return true;
1111 }
1112 } // else the stored pw is nonempty but the candidate is empty; no match
1113 }
1114 return false;
1115 }
1116
1117 @Override
1118 public boolean setBackupPassword(String currentPw, String newPw) {
1119 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1120 "setBackupPassword");
1121
1122 // If the supplied pw doesn't hash to the the saved one, fail
1123 if (!passwordMatchesSaved(currentPw, PBKDF2_HASH_ROUNDS)) {
1124 return false;
1125 }
1126
1127 // Clearing the password is okay
1128 if (newPw == null || newPw.isEmpty()) {
1129 if (mPasswordHashFile.exists()) {
1130 if (!mPasswordHashFile.delete()) {
1131 // Unable to delete the old pw file, so fail
1132 Slog.e(TAG, "Unable to clear backup password");
1133 return false;
1134 }
1135 }
1136 mPasswordHash = null;
1137 mPasswordSalt = null;
1138 return true;
1139 }
1140
1141 try {
1142 // Okay, build the hash of the new backup password
1143 byte[] salt = randomBytes(PBKDF2_SALT_SIZE);
1144 String newPwHash = buildPasswordHash(newPw, salt, PBKDF2_HASH_ROUNDS);
1145
1146 OutputStream pwf = null, buffer = null;
1147 DataOutputStream out = null;
1148 try {
1149 pwf = new FileOutputStream(mPasswordHashFile);
1150 buffer = new BufferedOutputStream(pwf);
1151 out = new DataOutputStream(buffer);
1152 // integer length of the salt array, followed by the salt,
1153 // then the hex pw hash string
1154 out.writeInt(salt.length);
1155 out.write(salt);
1156 out.writeUTF(newPwHash);
1157 out.flush();
1158 mPasswordHash = newPwHash;
1159 mPasswordSalt = salt;
1160 return true;
1161 } finally {
1162 if (out != null) out.close();
1163 if (buffer != null) buffer.close();
1164 if (pwf != null) pwf.close();
1165 }
1166 } catch (IOException e) {
1167 Slog.e(TAG, "Unable to set backup password");
1168 }
1169 return false;
1170 }
1171
1172 @Override
1173 public boolean hasBackupPassword() {
1174 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1175 "hasBackupPassword");
Christopher Tate32418be2011-10-10 13:51:12 -07001176
1177 try {
1178 return (mMountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE)
1179 || (mPasswordHash != null && mPasswordHash.length() > 0);
1180 } catch (Exception e) {
1181 // If we can't talk to the mount service we have a serious problem; fail
1182 // "secure" i.e. assuming that we require a password
1183 return true;
1184 }
Christopher Tate2efd2db2011-07-19 16:32:49 -07001185 }
1186
Christopher Tate4cc86e12009-09-21 19:36:51 -07001187 // Maintain persistent state around whether need to do an initialize operation.
1188 // Must be called with the queue lock held.
1189 void recordInitPendingLocked(boolean isPending, String transportName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001190 if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
Christopher Tate4cc86e12009-09-21 19:36:51 -07001191 + " on transport " + transportName);
1192 try {
1193 IBackupTransport transport = getTransport(transportName);
1194 String transportDirName = transport.transportDirName();
1195 File stateDir = new File(mBaseStateDir, transportDirName);
1196 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1197
1198 if (isPending) {
1199 // We need an init before we can proceed with sending backup data.
1200 // Record that with an entry in our set of pending inits, as well as
1201 // journaling it via creation of a sentinel file.
1202 mPendingInits.add(transportName);
1203 try {
1204 (new FileOutputStream(initPendingFile)).close();
1205 } catch (IOException ioe) {
1206 // Something is badly wrong with our permissions; just try to move on
1207 }
1208 } else {
1209 // No more initialization needed; wipe the journal and reset our state.
1210 initPendingFile.delete();
1211 mPendingInits.remove(transportName);
1212 }
1213 } catch (RemoteException e) {
1214 // can't happen; the transport is local
1215 }
1216 }
1217
Christopher Tated55e18a2009-09-21 10:12:59 -07001218 // Reset all of our bookkeeping, in response to having been told that
1219 // the backend data has been wiped [due to idle expiry, for example],
1220 // so we must re-upload all saved settings.
1221 void resetBackupState(File stateFileDir) {
1222 synchronized (mQueueLock) {
1223 // Wipe the "what we've ever backed up" tracking
Christopher Tated55e18a2009-09-21 10:12:59 -07001224 mEverStoredApps.clear();
Dan Egnor852f8e42009-09-30 11:20:45 -07001225 mEverStored.delete();
Christopher Tated55e18a2009-09-21 10:12:59 -07001226
Christopher Tate84725812010-02-04 15:52:40 -08001227 mCurrentToken = 0;
1228 writeRestoreTokens();
1229
Christopher Tated55e18a2009-09-21 10:12:59 -07001230 // Remove all the state files
1231 for (File sf : stateFileDir.listFiles()) {
Christopher Tate4cc86e12009-09-21 19:36:51 -07001232 // ... but don't touch the needs-init sentinel
1233 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
1234 sf.delete();
1235 }
Christopher Tated55e18a2009-09-21 10:12:59 -07001236 }
Christopher Tate45597642011-04-04 16:59:21 -07001237 }
Christopher Tated55e18a2009-09-21 10:12:59 -07001238
Christopher Tate45597642011-04-04 16:59:21 -07001239 // Enqueue a new backup of every participant
Christopher Tate8e294d42011-08-31 20:37:12 -07001240 synchronized (mBackupParticipants) {
Christopher Tatea3d55342012-03-27 13:16:18 -07001241 final int N = mBackupParticipants.size();
Christopher Tate8e294d42011-08-31 20:37:12 -07001242 for (int i=0; i<N; i++) {
Christopher Tatea3d55342012-03-27 13:16:18 -07001243 HashSet<String> participants = mBackupParticipants.valueAt(i);
1244 if (participants != null) {
1245 for (String packageName : participants) {
1246 dataChangedImpl(packageName);
1247 }
Christopher Tate8e294d42011-08-31 20:37:12 -07001248 }
Christopher Tated55e18a2009-09-21 10:12:59 -07001249 }
1250 }
1251 }
1252
Christopher Tatedfa47b56e2009-12-22 16:01:32 -08001253 // Add a transport to our set of available backends. If 'transport' is null, this
1254 // is an unregistration, and the transport's entry is removed from our bookkeeping.
Christopher Tate91717492009-06-26 21:07:13 -07001255 private void registerTransport(String name, IBackupTransport transport) {
1256 synchronized (mTransports) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001257 if (DEBUG) Slog.v(TAG, "Registering transport " + name + " = " + transport);
Christopher Tatedfa47b56e2009-12-22 16:01:32 -08001258 if (transport != null) {
1259 mTransports.put(name, transport);
1260 } else {
1261 mTransports.remove(name);
Christopher Tateb0dcaaf2010-01-29 16:27:04 -08001262 if ((mCurrentTransport != null) && mCurrentTransport.equals(name)) {
Christopher Tatedfa47b56e2009-12-22 16:01:32 -08001263 mCurrentTransport = null;
1264 }
1265 // Nothing further to do in the unregistration case
1266 return;
1267 }
Christopher Tate91717492009-06-26 21:07:13 -07001268 }
Christopher Tate4cc86e12009-09-21 19:36:51 -07001269
1270 // If the init sentinel file exists, we need to be sure to perform the init
1271 // as soon as practical. We also create the state directory at registration
1272 // time to ensure it's present from the outset.
1273 try {
1274 String transportName = transport.transportDirName();
1275 File stateDir = new File(mBaseStateDir, transportName);
1276 stateDir.mkdirs();
1277
1278 File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1279 if (initSentinel.exists()) {
1280 synchronized (mQueueLock) {
1281 mPendingInits.add(transportName);
1282
1283 // TODO: pick a better starting time than now + 1 minute
1284 long delay = 1000 * 60; // one minute, in milliseconds
1285 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1286 System.currentTimeMillis() + delay, mRunInitIntent);
1287 }
1288 }
1289 } catch (RemoteException e) {
1290 // can't happen, the transport is local
1291 }
Christopher Tate91717492009-06-26 21:07:13 -07001292 }
1293
Christopher Tate3799bc22009-05-06 16:13:56 -07001294 // ----- Track installation/removal of packages -----
1295 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1296 public void onReceive(Context context, Intent intent) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001297 if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
Christopher Tate3799bc22009-05-06 16:13:56 -07001298
Christopher Tate3799bc22009-05-06 16:13:56 -07001299 String action = intent.getAction();
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001300 boolean replacing = false;
1301 boolean added = false;
1302 Bundle extras = intent.getExtras();
1303 String pkgList[] = null;
1304 if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
Christopher Tatea3d55342012-03-27 13:16:18 -07001305 Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001306 Uri uri = intent.getData();
1307 if (uri == null) {
1308 return;
1309 }
1310 String pkgName = uri.getSchemeSpecificPart();
1311 if (pkgName != null) {
1312 pkgList = new String[] { pkgName };
1313 }
Christopher Tatea3d55342012-03-27 13:16:18 -07001314 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1315 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001316 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001317 added = true;
1318 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
Suchi Amalapurapub56ae202010-02-04 22:51:07 -08001319 } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001320 added = false;
1321 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1322 }
Christopher Tatecc55f812011-08-16 16:06:53 -07001323
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001324 if (pkgList == null || pkgList.length == 0) {
1325 return;
1326 }
Christopher Tatea3d55342012-03-27 13:16:18 -07001327
1328 final int uid = extras.getInt(Intent.EXTRA_UID);
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001329 if (added) {
Christopher Tate3799bc22009-05-06 16:13:56 -07001330 synchronized (mBackupParticipants) {
Christopher Tate0bacfd22012-01-11 14:41:19 -08001331 if (replacing) {
Christopher Tatea3d55342012-03-27 13:16:18 -07001332 // This is the package-replaced case; we just remove the entry
1333 // under the old uid and fall through to re-add.
1334 removePackageParticipantsLocked(pkgList, uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001335 }
Christopher Tatea3d55342012-03-27 13:16:18 -07001336 addPackageParticipantsLocked(pkgList);
Christopher Tate3799bc22009-05-06 16:13:56 -07001337 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001338 } else {
1339 if (replacing) {
Christopher Tate3799bc22009-05-06 16:13:56 -07001340 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
1341 } else {
1342 synchronized (mBackupParticipants) {
Christopher Tatea3d55342012-03-27 13:16:18 -07001343 removePackageParticipantsLocked(pkgList, uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001344 }
1345 }
1346 }
1347 }
1348 };
1349
Dan Egnor87a02bc2009-06-17 02:30:10 -07001350 // ----- Track connection to GoogleBackupTransport service -----
1351 ServiceConnection mGoogleConnection = new ServiceConnection() {
1352 public void onServiceConnected(ComponentName name, IBinder service) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001353 if (DEBUG) Slog.v(TAG, "Connected to Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -07001354 mGoogleTransport = IBackupTransport.Stub.asInterface(service);
Christopher Tate91717492009-06-26 21:07:13 -07001355 registerTransport(name.flattenToShortString(), mGoogleTransport);
Dan Egnor87a02bc2009-06-17 02:30:10 -07001356 }
1357
1358 public void onServiceDisconnected(ComponentName name) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001359 if (DEBUG) Slog.v(TAG, "Disconnected from Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -07001360 mGoogleTransport = null;
Christopher Tate91717492009-06-26 21:07:13 -07001361 registerTransport(name.flattenToShortString(), null);
Dan Egnor87a02bc2009-06-17 02:30:10 -07001362 }
1363 };
1364
Christopher Tate0bacfd22012-01-11 14:41:19 -08001365 // Add the backup agents in the given packages to our set of known backup participants.
1366 // If 'packageNames' is null, adds all backup agents in the whole system.
1367 void addPackageParticipantsLocked(String[] packageNames) {
Christopher Tate181fafa2009-05-14 11:12:14 -07001368 // Look for apps that define the android:backupAgent attribute
Dan Egnorefe52642009-06-24 00:16:33 -07001369 List<PackageInfo> targetApps = allAgentPackages();
Christopher Tate0bacfd22012-01-11 14:41:19 -08001370 if (packageNames != null) {
1371 if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
1372 for (String packageName : packageNames) {
1373 addPackageParticipantsLockedInner(packageName, targetApps);
1374 }
1375 } else {
1376 if (DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
1377 addPackageParticipantsLockedInner(null, targetApps);
1378 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001379 }
1380
Christopher Tate181fafa2009-05-14 11:12:14 -07001381 private void addPackageParticipantsLockedInner(String packageName,
Dan Egnorefe52642009-06-24 00:16:33 -07001382 List<PackageInfo> targetPkgs) {
Christopher Tatec58efa62011-08-01 19:20:14 -07001383 if (MORE_DEBUG) {
Christopher Tate0bacfd22012-01-11 14:41:19 -08001384 Slog.v(TAG, "Examining " + packageName + " for backup agent");
Christopher Tate181fafa2009-05-14 11:12:14 -07001385 }
1386
Dan Egnorefe52642009-06-24 00:16:33 -07001387 for (PackageInfo pkg : targetPkgs) {
1388 if (packageName == null || pkg.packageName.equals(packageName)) {
1389 int uid = pkg.applicationInfo.uid;
Christopher Tatea3d55342012-03-27 13:16:18 -07001390 HashSet<String> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -07001391 if (set == null) {
Christopher Tatea3d55342012-03-27 13:16:18 -07001392 set = new HashSet<String>();
Christopher Tate3799bc22009-05-06 16:13:56 -07001393 mBackupParticipants.put(uid, set);
1394 }
Christopher Tatea3d55342012-03-27 13:16:18 -07001395 set.add(pkg.packageName);
Christopher Tate0bacfd22012-01-11 14:41:19 -08001396 if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
Christopher Tate73e02522009-07-15 14:18:26 -07001397
1398 // If we've never seen this app before, schedule a backup for it
1399 if (!mEverStoredApps.contains(pkg.packageName)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001400 if (DEBUG) Slog.i(TAG, "New app " + pkg.packageName
Christopher Tate73e02522009-07-15 14:18:26 -07001401 + " never backed up; scheduling");
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07001402 dataChangedImpl(pkg.packageName);
Christopher Tate73e02522009-07-15 14:18:26 -07001403 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001404 }
Christopher Tate487529a2009-04-29 14:03:25 -07001405 }
1406 }
1407
Christopher Tate0bacfd22012-01-11 14:41:19 -08001408 // Remove the given packages' entries from our known active set.
Christopher Tatea3d55342012-03-27 13:16:18 -07001409 void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
Christopher Tate0bacfd22012-01-11 14:41:19 -08001410 if (packageNames == null) {
1411 Slog.w(TAG, "removePackageParticipants with null list");
1412 return;
Christopher Tate181fafa2009-05-14 11:12:14 -07001413 }
Christopher Tate0bacfd22012-01-11 14:41:19 -08001414
Christopher Tatea3d55342012-03-27 13:16:18 -07001415 if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
1416 + " #" + packageNames.length);
Christopher Tate0bacfd22012-01-11 14:41:19 -08001417 for (String pkg : packageNames) {
Christopher Tatea3d55342012-03-27 13:16:18 -07001418 // Known previous UID, so we know which package set to check
1419 HashSet<String> set = mBackupParticipants.get(oldUid);
1420 if (set != null && set.contains(pkg)) {
1421 removePackageFromSetLocked(set, pkg);
1422 if (set.isEmpty()) {
1423 if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set");
1424 mBackupParticipants.remove(oldUid);
1425 }
1426 }
Christopher Tate0bacfd22012-01-11 14:41:19 -08001427 }
Christopher Tate3799bc22009-05-06 16:13:56 -07001428 }
1429
Christopher Tatea3d55342012-03-27 13:16:18 -07001430 private void removePackageFromSetLocked(final HashSet<String> set,
1431 final String packageName) {
1432 if (set.contains(packageName)) {
1433 // Found it. Remove this one package from the bookkeeping, and
1434 // if it's the last participating app under this uid we drop the
1435 // (now-empty) set as well.
1436 if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
1437 removeEverBackedUp(packageName);
1438 set.remove(packageName);
Christopher Tate3799bc22009-05-06 16:13:56 -07001439 }
1440 }
1441
Christopher Tate181fafa2009-05-14 11:12:14 -07001442 // Returns the set of all applications that define an android:backupAgent attribute
Christopher Tate73e02522009-07-15 14:18:26 -07001443 List<PackageInfo> allAgentPackages() {
Christopher Tate6785dd82009-06-18 15:58:25 -07001444 // !!! TODO: cache this and regenerate only when necessary
Dan Egnorefe52642009-06-24 00:16:33 -07001445 int flags = PackageManager.GET_SIGNATURES;
1446 List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
1447 int N = packages.size();
1448 for (int a = N-1; a >= 0; a--) {
Christopher Tate0749dcd2009-08-13 15:13:03 -07001449 PackageInfo pkg = packages.get(a);
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001450 try {
1451 ApplicationInfo app = pkg.applicationInfo;
1452 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
Christopher Tatea87240c2010-02-12 14:12:34 -08001453 || app.backupAgentName == null) {
Christopher Tateb8eb1cb2009-09-16 10:57:21 -07001454 packages.remove(a);
1455 }
1456 else {
1457 // we will need the shared library path, so look that up and store it here
1458 app = mPackageManager.getApplicationInfo(pkg.packageName,
1459 PackageManager.GET_SHARED_LIBRARY_FILES);
1460 pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
1461 }
1462 } catch (NameNotFoundException e) {
Dan Egnorefe52642009-06-24 00:16:33 -07001463 packages.remove(a);
Christopher Tate181fafa2009-05-14 11:12:14 -07001464 }
1465 }
Dan Egnorefe52642009-06-24 00:16:33 -07001466 return packages;
Christopher Tate181fafa2009-05-14 11:12:14 -07001467 }
Christopher Tateaa088442009-06-16 18:25:46 -07001468
Christopher Tate84725812010-02-04 15:52:40 -08001469 // Called from the backup task: record that the given app has been successfully
Christopher Tate73e02522009-07-15 14:18:26 -07001470 // backed up at least once
1471 void logBackupComplete(String packageName) {
Dan Egnor852f8e42009-09-30 11:20:45 -07001472 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
1473
1474 synchronized (mEverStoredApps) {
1475 if (!mEverStoredApps.add(packageName)) return;
1476
1477 RandomAccessFile out = null;
1478 try {
1479 out = new RandomAccessFile(mEverStored, "rws");
1480 out.seek(out.length());
1481 out.writeUTF(packageName);
1482 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001483 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
Dan Egnor852f8e42009-09-30 11:20:45 -07001484 } finally {
1485 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tate73e02522009-07-15 14:18:26 -07001486 }
1487 }
1488 }
1489
Christopher Tatee97e8072009-07-15 16:45:50 -07001490 // Remove our awareness of having ever backed up the given package
1491 void removeEverBackedUp(String packageName) {
Christopher Tatec58efa62011-08-01 19:20:14 -07001492 if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
1493 if (MORE_DEBUG) Slog.v(TAG, "New set:");
Christopher Tatee97e8072009-07-15 16:45:50 -07001494
Dan Egnor852f8e42009-09-30 11:20:45 -07001495 synchronized (mEverStoredApps) {
1496 // Rewrite the file and rename to overwrite. If we reboot in the middle,
1497 // we'll recognize on initialization time that the package no longer
1498 // exists and fix it up then.
1499 File tempKnownFile = new File(mBaseStateDir, "processed.new");
1500 RandomAccessFile known = null;
1501 try {
1502 known = new RandomAccessFile(tempKnownFile, "rws");
1503 mEverStoredApps.remove(packageName);
1504 for (String s : mEverStoredApps) {
1505 known.writeUTF(s);
Christopher Tatec58efa62011-08-01 19:20:14 -07001506 if (MORE_DEBUG) Slog.v(TAG, " " + s);
Christopher Tatee97e8072009-07-15 16:45:50 -07001507 }
Dan Egnor852f8e42009-09-30 11:20:45 -07001508 known.close();
1509 known = null;
1510 if (!tempKnownFile.renameTo(mEverStored)) {
1511 throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
1512 }
1513 } catch (IOException e) {
1514 // Bad: we couldn't create the new copy. For safety's sake we
1515 // abandon the whole process and remove all what's-backed-up
1516 // state entirely, meaning we'll force a backup pass for every
1517 // participant on the next boot or [re]install.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001518 Slog.w(TAG, "Error rewriting " + mEverStored, e);
Dan Egnor852f8e42009-09-30 11:20:45 -07001519 mEverStoredApps.clear();
1520 tempKnownFile.delete();
1521 mEverStored.delete();
1522 } finally {
1523 try { if (known != null) known.close(); } catch (IOException e) {}
Christopher Tatee97e8072009-07-15 16:45:50 -07001524 }
1525 }
1526 }
1527
Christopher Tateb49ceb32010-02-08 16:22:24 -08001528 // Persistently record the current and ancestral backup tokens as well
1529 // as the set of packages with data [supposedly] available in the
1530 // ancestral dataset.
Christopher Tate84725812010-02-04 15:52:40 -08001531 void writeRestoreTokens() {
1532 try {
1533 RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
Christopher Tateb49ceb32010-02-08 16:22:24 -08001534
1535 // First, the version number of this record, for futureproofing
1536 af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
1537
1538 // Write the ancestral and current tokens
Christopher Tate84725812010-02-04 15:52:40 -08001539 af.writeLong(mAncestralToken);
1540 af.writeLong(mCurrentToken);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001541
1542 // Now write the set of ancestral packages
1543 if (mAncestralPackages == null) {
1544 af.writeInt(-1);
1545 } else {
1546 af.writeInt(mAncestralPackages.size());
Joe Onorato8a9b2202010-02-26 18:56:32 -08001547 if (DEBUG) Slog.v(TAG, "Ancestral packages: " + mAncestralPackages.size());
Christopher Tateb49ceb32010-02-08 16:22:24 -08001548 for (String pkgName : mAncestralPackages) {
1549 af.writeUTF(pkgName);
Christopher Tatec58efa62011-08-01 19:20:14 -07001550 if (MORE_DEBUG) Slog.v(TAG, " " + pkgName);
Christopher Tateb49ceb32010-02-08 16:22:24 -08001551 }
1552 }
Christopher Tate84725812010-02-04 15:52:40 -08001553 af.close();
1554 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001555 Slog.w(TAG, "Unable to write token file:", e);
Christopher Tate84725812010-02-04 15:52:40 -08001556 }
1557 }
1558
Dan Egnor87a02bc2009-06-17 02:30:10 -07001559 // Return the given transport
Christopher Tate91717492009-06-26 21:07:13 -07001560 private IBackupTransport getTransport(String transportName) {
1561 synchronized (mTransports) {
1562 IBackupTransport transport = mTransports.get(transportName);
1563 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001564 Slog.w(TAG, "Requested unavailable transport: " + transportName);
Christopher Tate91717492009-06-26 21:07:13 -07001565 }
1566 return transport;
Christopher Tate8c850b72009-06-07 19:33:20 -07001567 }
Christopher Tate8c850b72009-06-07 19:33:20 -07001568 }
1569
Christopher Tatedf01dea2009-06-09 20:45:02 -07001570 // fire off a backup agent, blocking until it attaches or times out
1571 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
1572 IBackupAgent agent = null;
1573 synchronized(mAgentConnectLock) {
1574 mConnecting = true;
1575 mConnectedAgent = null;
1576 try {
1577 if (mActivityManager.bindBackupAgent(app, mode)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001578 Slog.d(TAG, "awaiting agent for " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001579
1580 // success; wait for the agent to arrive
Christopher Tate75a99702011-05-18 16:28:19 -07001581 // only wait 10 seconds for the bind to happen
Christopher Tatec7b31e32009-06-10 15:49:30 -07001582 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1583 while (mConnecting && mConnectedAgent == null
1584 && (System.currentTimeMillis() < timeoutMark)) {
Christopher Tatedf01dea2009-06-09 20:45:02 -07001585 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001586 mAgentConnectLock.wait(5000);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001587 } catch (InterruptedException e) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001588 // just bail
Christopher Tate6de74ff2012-01-17 15:20:32 -08001589 if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001590 return null;
1591 }
1592 }
1593
1594 // if we timed out with no connect, abort and move on
1595 if (mConnecting == true) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001596 Slog.w(TAG, "Timeout waiting for agent " + app);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001597 return null;
1598 }
Christopher Tate6de74ff2012-01-17 15:20:32 -08001599 if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
Christopher Tatedf01dea2009-06-09 20:45:02 -07001600 agent = mConnectedAgent;
1601 }
1602 } catch (RemoteException e) {
1603 // can't happen
1604 }
1605 }
1606 return agent;
1607 }
1608
Christopher Tatec7b31e32009-06-10 15:49:30 -07001609 // clear an application's data, blocking until the operation completes or times out
1610 void clearApplicationDataSynchronous(String packageName) {
Christopher Tatef7c886b2009-06-26 15:34:09 -07001611 // Don't wipe packages marked allowClearUserData=false
1612 try {
1613 PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
1614 if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
Christopher Tatec58efa62011-08-01 19:20:14 -07001615 if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
Christopher Tatef7c886b2009-06-26 15:34:09 -07001616 + packageName);
1617 return;
1618 }
1619 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001620 Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
Christopher Tatef7c886b2009-06-26 15:34:09 -07001621 return;
1622 }
1623
Christopher Tatec7b31e32009-06-10 15:49:30 -07001624 ClearDataObserver observer = new ClearDataObserver();
1625
1626 synchronized(mClearDataLock) {
1627 mClearingData = true;
Christopher Tate9dfdac52009-08-06 14:57:53 -07001628 try {
Amith Yamasani742a6712011-05-04 14:49:28 -07001629 mActivityManager.clearApplicationUserData(packageName, observer,
1630 Binder.getOrigCallingUser());
Christopher Tate9dfdac52009-08-06 14:57:53 -07001631 } catch (RemoteException e) {
1632 // can't happen because the activity manager is in this process
1633 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07001634
1635 // only wait 10 seconds for the clear data to happen
1636 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
1637 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
1638 try {
1639 mClearDataLock.wait(5000);
1640 } catch (InterruptedException e) {
1641 // won't happen, but still.
1642 mClearingData = false;
1643 }
1644 }
1645 }
1646 }
1647
1648 class ClearDataObserver extends IPackageDataObserver.Stub {
Dan Egnor852f8e42009-09-30 11:20:45 -07001649 public void onRemoveCompleted(String packageName, boolean succeeded) {
Christopher Tatec7b31e32009-06-10 15:49:30 -07001650 synchronized(mClearDataLock) {
1651 mClearingData = false;
Christopher Tatef68eb502009-06-16 11:02:01 -07001652 mClearDataLock.notifyAll();
Christopher Tatec7b31e32009-06-10 15:49:30 -07001653 }
1654 }
1655 }
1656
Christopher Tate1bb69062010-02-19 17:02:12 -08001657 // Get the restore-set token for the best-available restore set for this package:
1658 // the active set if possible, else the ancestral one. Returns zero if none available.
1659 long getAvailableRestoreToken(String packageName) {
1660 long token = mAncestralToken;
1661 synchronized (mQueueLock) {
1662 if (mEverStoredApps.contains(packageName)) {
1663 token = mCurrentToken;
1664 }
1665 }
1666 return token;
1667 }
1668
Christopher Tate44a27902010-01-27 17:15:49 -08001669 // -----
Christopher Tate8e294d42011-08-31 20:37:12 -07001670 // Interface and methods used by the asynchronous-with-timeout backup/restore operations
1671
1672 interface BackupRestoreTask {
1673 // Execute one tick of whatever state machine the task implements
1674 void execute();
1675
1676 // An operation that wanted a callback has completed
1677 void operationComplete();
1678
1679 // An operation that wanted a callback has timed out
1680 void handleTimeout();
1681 }
1682
1683 void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) {
1684 if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
1685 + " interval=" + interval);
Christopher Tate44a27902010-01-27 17:15:49 -08001686 synchronized (mCurrentOpLock) {
Christopher Tate8e294d42011-08-31 20:37:12 -07001687 mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
1688
1689 Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
1690 mBackupHandler.sendMessageDelayed(msg, interval);
1691 }
1692 }
1693
1694 // synchronous waiter case
1695 boolean waitUntilOperationComplete(int token) {
1696 if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
1697 + Integer.toHexString(token));
1698 int finalState = OP_PENDING;
1699 Operation op = null;
1700 synchronized (mCurrentOpLock) {
1701 while (true) {
1702 op = mCurrentOperations.get(token);
1703 if (op == null) {
1704 // mysterious disappearance: treat as success with no callback
1705 break;
1706 } else {
1707 if (op.state == OP_PENDING) {
1708 try {
1709 mCurrentOpLock.wait();
1710 } catch (InterruptedException e) {}
1711 // When the wait is notified we loop around and recheck the current state
1712 } else {
1713 // No longer pending; we're done
1714 finalState = op.state;
1715 break;
1716 }
Christopher Tate44a27902010-01-27 17:15:49 -08001717 }
Christopher Tate44a27902010-01-27 17:15:49 -08001718 }
1719 }
Christopher Tate8e294d42011-08-31 20:37:12 -07001720
Christopher Tate44a27902010-01-27 17:15:49 -08001721 mBackupHandler.removeMessages(MSG_TIMEOUT);
Christopher Tatec58efa62011-08-01 19:20:14 -07001722 if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
Christopher Tate1bb69062010-02-19 17:02:12 -08001723 + " complete: finalState=" + finalState);
Christopher Tate44a27902010-01-27 17:15:49 -08001724 return finalState == OP_ACKNOWLEDGED;
1725 }
1726
Christopher Tate8e294d42011-08-31 20:37:12 -07001727 void handleTimeout(int token, Object obj) {
1728 // Notify any synchronous waiters
1729 Operation op = null;
Christopher Tate4a627c72011-04-01 14:43:32 -07001730 synchronized (mCurrentOpLock) {
Christopher Tate8e294d42011-08-31 20:37:12 -07001731 op = mCurrentOperations.get(token);
1732 if (MORE_DEBUG) {
1733 if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
1734 + " but no op found");
1735 }
1736 int state = (op != null) ? op.state : OP_TIMEOUT;
1737 if (state == OP_PENDING) {
1738 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
1739 op.state = OP_TIMEOUT;
1740 mCurrentOperations.put(token, op);
1741 }
1742 mCurrentOpLock.notifyAll();
1743 }
1744
1745 // If there's a TimeoutHandler for this event, call it
1746 if (op != null && op.callback != null) {
1747 op.callback.handleTimeout();
Christopher Tate4a627c72011-04-01 14:43:32 -07001748 }
Christopher Tate44a27902010-01-27 17:15:49 -08001749 }
1750
Christopher Tate043dadc2009-06-02 16:11:00 -07001751 // ----- Back up a set of applications via a worker thread -----
1752
Christopher Tate8e294d42011-08-31 20:37:12 -07001753 enum BackupState {
1754 INITIAL,
1755 RUNNING_QUEUE,
1756 FINAL
1757 }
1758
1759 class PerformBackupTask implements BackupRestoreTask {
1760 private static final String TAG = "PerformBackupTask";
1761
Christopher Tateaa088442009-06-16 18:25:46 -07001762 IBackupTransport mTransport;
Christopher Tate043dadc2009-06-02 16:11:00 -07001763 ArrayList<BackupRequest> mQueue;
Christopher Tate8e294d42011-08-31 20:37:12 -07001764 ArrayList<BackupRequest> mOriginalQueue;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001765 File mStateDir;
Christopher Tatecde87f42009-06-12 12:55:53 -07001766 File mJournal;
Christopher Tate8e294d42011-08-31 20:37:12 -07001767 BackupState mCurrentState;
1768
1769 // carried information about the current in-flight operation
1770 PackageInfo mCurrentPackage;
1771 File mSavedStateName;
1772 File mBackupDataName;
1773 File mNewStateName;
1774 ParcelFileDescriptor mSavedState;
1775 ParcelFileDescriptor mBackupData;
1776 ParcelFileDescriptor mNewState;
1777 int mStatus;
1778 boolean mFinished;
Christopher Tate043dadc2009-06-02 16:11:00 -07001779
Christopher Tate44a27902010-01-27 17:15:49 -08001780 public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue,
Christopher Tatecde87f42009-06-12 12:55:53 -07001781 File journal) {
Christopher Tateaa088442009-06-16 18:25:46 -07001782 mTransport = transport;
Christopher Tate8e294d42011-08-31 20:37:12 -07001783 mOriginalQueue = queue;
Christopher Tatecde87f42009-06-12 12:55:53 -07001784 mJournal = journal;
Christopher Tate5cb400b2009-06-25 16:03:14 -07001785
1786 try {
1787 mStateDir = new File(mBaseStateDir, transport.transportDirName());
1788 } catch (RemoteException e) {
1789 // can't happen; the transport is local
1790 }
Christopher Tate8e294d42011-08-31 20:37:12 -07001791
1792 mCurrentState = BackupState.INITIAL;
1793 mFinished = false;
Christopher Tate6de74ff2012-01-17 15:20:32 -08001794
1795 addBackupTrace("STATE => INITIAL");
Christopher Tate043dadc2009-06-02 16:11:00 -07001796 }
1797
Christopher Tate8e294d42011-08-31 20:37:12 -07001798 // Main entry point: perform one chunk of work, updating the state as appropriate
1799 // and reposting the next chunk to the primary backup handler thread.
1800 @Override
1801 public void execute() {
1802 switch (mCurrentState) {
1803 case INITIAL:
1804 beginBackup();
1805 break;
1806
1807 case RUNNING_QUEUE:
1808 invokeNextAgent();
1809 break;
1810
1811 case FINAL:
1812 if (!mFinished) finalizeBackup();
1813 else {
1814 Slog.e(TAG, "Duplicate finish");
1815 }
Christopher Tate2982d062011-09-06 20:35:24 -07001816 mFinished = true;
Christopher Tate8e294d42011-08-31 20:37:12 -07001817 break;
1818 }
1819 }
1820
1821 // We're starting a backup pass. Initialize the transport and send
1822 // the PM metadata blob if we haven't already.
1823 void beginBackup() {
Christopher Tate6de74ff2012-01-17 15:20:32 -08001824 if (DEBUG_BACKUP_TRACE) {
1825 clearBackupTrace();
1826 StringBuilder b = new StringBuilder(256);
1827 b.append("beginBackup: [");
1828 for (BackupRequest req : mOriginalQueue) {
1829 b.append(' ');
1830 b.append(req.packageName);
1831 }
1832 b.append(" ]");
1833 addBackupTrace(b.toString());
1834 }
1835
Christopher Tate8e294d42011-08-31 20:37:12 -07001836 mStatus = BackupConstants.TRANSPORT_OK;
1837
1838 // Sanity check: if the queue is empty we have no work to do.
1839 if (mOriginalQueue.isEmpty()) {
1840 Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
Christopher Tate6de74ff2012-01-17 15:20:32 -08001841 addBackupTrace("queue empty at begin");
1842 executeNextState(BackupState.FINAL);
Christopher Tate8e294d42011-08-31 20:37:12 -07001843 return;
1844 }
1845
1846 // We need to retain the original queue contents in case of transport
1847 // failure, but we want a working copy that we can manipulate along
1848 // the way.
1849 mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
1850
Joe Onorato8a9b2202010-02-26 18:56:32 -08001851 if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
Christopher Tate043dadc2009-06-02 16:11:00 -07001852
Christopher Tate8e294d42011-08-31 20:37:12 -07001853 File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
Christopher Tate043dadc2009-06-02 16:11:00 -07001854 try {
Christopher Tate6de74ff2012-01-17 15:20:32 -08001855 final String transportName = mTransport.transportDirName();
1856 EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
Dan Egnor01445162009-09-21 17:04:05 -07001857
Dan Egnor852f8e42009-09-30 11:20:45 -07001858 // If we haven't stored package manager metadata yet, we must init the transport.
Christopher Tate8e294d42011-08-31 20:37:12 -07001859 if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001860 Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
Christopher Tate6de74ff2012-01-17 15:20:32 -08001861 addBackupTrace("initializing transport " + transportName);
Dan Egnor852f8e42009-09-30 11:20:45 -07001862 resetBackupState(mStateDir); // Just to make sure.
Christopher Tate8e294d42011-08-31 20:37:12 -07001863 mStatus = mTransport.initializeDevice();
Christopher Tate6de74ff2012-01-17 15:20:32 -08001864
1865 addBackupTrace("transport.initializeDevice() == " + mStatus);
Christopher Tate8e294d42011-08-31 20:37:12 -07001866 if (mStatus == BackupConstants.TRANSPORT_OK) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001867 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07001868 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001869 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Joe Onorato8a9b2202010-02-26 18:56:32 -08001870 Slog.e(TAG, "Transport error in initializeDevice()");
Dan Egnor726247c2009-09-29 19:12:31 -07001871 }
Dan Egnor01445162009-09-21 17:04:05 -07001872 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07001873
1874 // The package manager doesn't have a proper <application> etc, but since
1875 // it's running here in the system process we can just set up its agent
1876 // directly and use a synthetic BackupRequest. We always run this pass
1877 // because it's cheap and this way we guarantee that we don't get out of
1878 // step even if we're selecting among various transports at run time.
Christopher Tate8e294d42011-08-31 20:37:12 -07001879 if (mStatus == BackupConstants.TRANSPORT_OK) {
Dan Egnor01445162009-09-21 17:04:05 -07001880 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
1881 mPackageManager, allAgentPackages());
Christopher Tate8e294d42011-08-31 20:37:12 -07001882 mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
Dan Egnor01445162009-09-21 17:04:05 -07001883 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
Christopher Tate6de74ff2012-01-17 15:20:32 -08001884 addBackupTrace("PMBA invoke: " + mStatus);
Dan Egnor01445162009-09-21 17:04:05 -07001885 }
Christopher Tate90967f42009-09-20 15:28:33 -07001886
Christopher Tate8e294d42011-08-31 20:37:12 -07001887 if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
1888 // The backend reports that our dataset has been wiped. Note this in
1889 // the event log; the no-success code below will reset the backup
1890 // state as well.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001891 EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
Dan Egnorbb9001c2009-07-27 12:20:13 -07001892 }
1893 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001894 Slog.e(TAG, "Error in backup thread", e);
Christopher Tate6de74ff2012-01-17 15:20:32 -08001895 addBackupTrace("Exception in backup thread: " + e);
Christopher Tate8e294d42011-08-31 20:37:12 -07001896 mStatus = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07001897 } finally {
Christopher Tate8e294d42011-08-31 20:37:12 -07001898 // If we've succeeded so far, invokeAgentForBackup() will have run the PM
1899 // metadata and its completion/timeout callback will continue the state
1900 // machine chain. If it failed that won't happen; we handle that now.
Christopher Tate6de74ff2012-01-17 15:20:32 -08001901 addBackupTrace("exiting prelim: " + mStatus);
Christopher Tate8e294d42011-08-31 20:37:12 -07001902 if (mStatus != BackupConstants.TRANSPORT_OK) {
1903 // if things went wrong at this point, we need to
1904 // restage everything and try again later.
1905 resetBackupState(mStateDir); // Just to make sure.
1906 executeNextState(BackupState.FINAL);
Christopher Tate84725812010-02-04 15:52:40 -08001907 }
Christopher Tatecde87f42009-06-12 12:55:53 -07001908 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001909 }
1910
Christopher Tate8e294d42011-08-31 20:37:12 -07001911 // Transport has been initialized and the PM metadata submitted successfully
1912 // if that was warranted. Now we process the single next thing in the queue.
1913 void invokeNextAgent() {
1914 mStatus = BackupConstants.TRANSPORT_OK;
Christopher Tate6de74ff2012-01-17 15:20:32 -08001915 addBackupTrace("invoke q=" + mQueue.size());
Christopher Tate043dadc2009-06-02 16:11:00 -07001916
Christopher Tate8e294d42011-08-31 20:37:12 -07001917 // Sanity check that we have work to do. If not, skip to the end where
1918 // we reestablish the wakelock invariants etc.
1919 if (mQueue.isEmpty()) {
Christopher Tate6de74ff2012-01-17 15:20:32 -08001920 if (DEBUG) Slog.i(TAG, "queue now empty");
Christopher Tate8e294d42011-08-31 20:37:12 -07001921 executeNextState(BackupState.FINAL);
1922 return;
1923 }
1924
1925 // pop the entry we're going to process on this step
1926 BackupRequest request = mQueue.get(0);
1927 mQueue.remove(0);
1928
1929 Slog.d(TAG, "starting agent for backup of " + request);
Christopher Tate6de74ff2012-01-17 15:20:32 -08001930 addBackupTrace("launch agent for " + request.packageName);
Christopher Tate8e294d42011-08-31 20:37:12 -07001931
1932 // Verify that the requested app exists; it might be something that
1933 // requested a backup but was then uninstalled. The request was
1934 // journalled and rather than tamper with the journal it's safer
1935 // to sanity-check here. This also gives us the classname of the
1936 // package's backup agent.
1937 try {
1938 mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
1939 PackageManager.GET_SIGNATURES);
Christopher Tate9c2efb32012-03-23 13:00:05 -07001940 if (mCurrentPackage.applicationInfo.backupAgentName == null) {
1941 // The manifest has changed but we had a stale backup request pending.
1942 // This won't happen again because the app won't be requesting further
1943 // backups.
1944 Slog.i(TAG, "Package " + request.packageName
1945 + " no longer supports backup; skipping");
1946 addBackupTrace("skipping - no agent, completion is noop");
1947 executeNextState(BackupState.RUNNING_QUEUE);
1948 return;
1949 }
Christopher Tatec28083a2010-12-14 16:16:44 -08001950
Christopher Tate043dadc2009-06-02 16:11:00 -07001951 IBackupAgent agent = null;
Christopher Tate043dadc2009-06-02 16:11:00 -07001952 try {
Christopher Tate8e294d42011-08-31 20:37:12 -07001953 mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
1954 agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
Christopher Tate4a627c72011-04-01 14:43:32 -07001955 IApplicationThread.BACKUP_MODE_INCREMENTAL);
Christopher Tate6de74ff2012-01-17 15:20:32 -08001956 addBackupTrace("agent bound; a? = " + (agent != null));
Christopher Tatedf01dea2009-06-09 20:45:02 -07001957 if (agent != null) {
Christopher Tate8e294d42011-08-31 20:37:12 -07001958 mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
1959 // at this point we'll either get a completion callback from the
1960 // agent, or a timeout message on the main handler. either way, we're
1961 // done here as long as we're successful so far.
1962 } else {
1963 // Timeout waiting for the agent
1964 mStatus = BackupConstants.AGENT_ERROR;
Christopher Tate043dadc2009-06-02 16:11:00 -07001965 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001966 } catch (SecurityException ex) {
1967 // Try for the next one.
Joe Onorato8a9b2202010-02-26 18:56:32 -08001968 Slog.d(TAG, "error in bind/backup", ex);
Christopher Tate8e294d42011-08-31 20:37:12 -07001969 mStatus = BackupConstants.AGENT_ERROR;
Christopher Tate6de74ff2012-01-17 15:20:32 -08001970 addBackupTrace("agent SE");
Christopher Tate8e294d42011-08-31 20:37:12 -07001971 }
1972 } catch (NameNotFoundException e) {
1973 Slog.d(TAG, "Package does not exist; skipping");
Christopher Tate6de74ff2012-01-17 15:20:32 -08001974 addBackupTrace("no such package");
1975 mStatus = BackupConstants.AGENT_UNKNOWN;
Christopher Tate8e294d42011-08-31 20:37:12 -07001976 } finally {
1977 mWakelock.setWorkSource(null);
1978
1979 // If there was an agent error, no timeout/completion handling will occur.
Christopher Tate6de74ff2012-01-17 15:20:32 -08001980 // That means we need to direct to the next state ourselves.
Christopher Tate8e294d42011-08-31 20:37:12 -07001981 if (mStatus != BackupConstants.TRANSPORT_OK) {
1982 BackupState nextState = BackupState.RUNNING_QUEUE;
1983
1984 // An agent-level failure means we reenqueue this one agent for
1985 // a later retry, but otherwise proceed normally.
1986 if (mStatus == BackupConstants.AGENT_ERROR) {
1987 if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
1988 + " - restaging");
1989 dataChangedImpl(request.packageName);
1990 mStatus = BackupConstants.TRANSPORT_OK;
1991 if (mQueue.isEmpty()) nextState = BackupState.FINAL;
Christopher Tate6de74ff2012-01-17 15:20:32 -08001992 } else if (mStatus == BackupConstants.AGENT_UNKNOWN) {
1993 // Failed lookup of the app, so we couldn't bring up an agent, but
1994 // we're otherwise fine. Just drop it and go on to the next as usual.
1995 mStatus = BackupConstants.TRANSPORT_OK;
1996 } else {
Christopher Tate8e294d42011-08-31 20:37:12 -07001997 // Transport-level failure means we reenqueue everything
1998 revertAndEndBackup();
1999 nextState = BackupState.FINAL;
2000 }
2001
2002 executeNextState(nextState);
Christopher Tate6de74ff2012-01-17 15:20:32 -08002003 } else {
2004 addBackupTrace("expecting completion/timeout callback");
Christopher Tate043dadc2009-06-02 16:11:00 -07002005 }
2006 }
2007 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07002008
Christopher Tate8e294d42011-08-31 20:37:12 -07002009 void finalizeBackup() {
Christopher Tate6de74ff2012-01-17 15:20:32 -08002010 addBackupTrace("finishing");
2011
Christopher Tate8e294d42011-08-31 20:37:12 -07002012 // Either backup was successful, in which case we of course do not need
2013 // this pass's journal any more; or it failed, in which case we just
2014 // re-enqueued all of these packages in the current active journal.
2015 // Either way, we no longer need this pass's journal.
2016 if (mJournal != null && !mJournal.delete()) {
2017 Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
2018 }
2019
2020 // If everything actually went through and this is the first time we've
2021 // done a backup, we can now record what the current backup dataset token
2022 // is.
2023 if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) {
Christopher Tate6de74ff2012-01-17 15:20:32 -08002024 addBackupTrace("success; recording token");
Christopher Tate8e294d42011-08-31 20:37:12 -07002025 try {
2026 mCurrentToken = mTransport.getCurrentRestoreSet();
2027 } catch (RemoteException e) {} // can't happen
2028 writeRestoreTokens();
2029 }
2030
Christopher Tate336a6492011-10-05 16:05:43 -07002031 // Set up the next backup pass - at this point we can set mBackupRunning
2032 // to false to allow another pass to fire, because we're done with the
2033 // state machine sequence and the wakelock is refcounted.
2034 synchronized (mQueueLock) {
2035 mBackupRunning = false;
2036 if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
Christopher Tatee659fb92011-10-10 16:34:50 -07002037 // Make sure we back up everything and perform the one-time init
2038 clearMetadata();
2039 if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
Christopher Tate6de74ff2012-01-17 15:20:32 -08002040 addBackupTrace("init required; rerunning");
Christopher Tate336a6492011-10-05 16:05:43 -07002041 backupNow();
2042 }
Christopher Tate8e294d42011-08-31 20:37:12 -07002043 }
2044
2045 // Only once we're entirely finished do we release the wakelock
Christopher Tate6de74ff2012-01-17 15:20:32 -08002046 clearBackupTrace();
Christopher Tate8e294d42011-08-31 20:37:12 -07002047 Slog.i(TAG, "Backup pass finished.");
2048 mWakelock.release();
2049 }
2050
Christopher Tatee659fb92011-10-10 16:34:50 -07002051 // Remove the PM metadata state. This will generate an init on the next pass.
2052 void clearMetadata() {
2053 final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2054 if (pmState.exists()) pmState.delete();
2055 }
2056
Christopher Tate8e294d42011-08-31 20:37:12 -07002057 // Invoke an agent's doBackup() and start a timeout message spinning on the main
2058 // handler in case it doesn't get back to us.
2059 int invokeAgentForBackup(String packageName, IBackupAgent agent,
Dan Egnor01445162009-09-21 17:04:05 -07002060 IBackupTransport transport) {
Christopher Tate6de74ff2012-01-17 15:20:32 -08002061 if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
2062 addBackupTrace("invoking " + packageName);
Christopher Tatec7b31e32009-06-10 15:49:30 -07002063
Christopher Tate8e294d42011-08-31 20:37:12 -07002064 mSavedStateName = new File(mStateDir, packageName);
2065 mBackupDataName = new File(mDataDir, packageName + ".data");
2066 mNewStateName = new File(mStateDir, packageName + ".new");
Dan Egnorbb9001c2009-07-27 12:20:13 -07002067
Christopher Tate8e294d42011-08-31 20:37:12 -07002068 mSavedState = null;
2069 mBackupData = null;
2070 mNewState = null;
Dan Egnorbb9001c2009-07-27 12:20:13 -07002071
Christopher Tate4a627c72011-04-01 14:43:32 -07002072 final int token = generateToken();
Christopher Tatec7b31e32009-06-10 15:49:30 -07002073 try {
2074 // Look up the package info & signatures. This is first so that if it
2075 // throws an exception, there's no file setup yet that would need to
2076 // be unraveled.
Christopher Tateabce4e82009-06-18 18:35:32 -07002077 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Christopher Tate8e294d42011-08-31 20:37:12 -07002078 // The metadata 'package' is synthetic; construct one and make
2079 // sure our global state is pointed at it
2080 mCurrentPackage = new PackageInfo();
2081 mCurrentPackage.packageName = packageName;
Christopher Tateabce4e82009-06-18 18:35:32 -07002082 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07002083
Christopher Tatec7b31e32009-06-10 15:49:30 -07002084 // In a full backup, we pass a null ParcelFileDescriptor as
Christopher Tate4a627c72011-04-01 14:43:32 -07002085 // the saved-state "file". This is by definition an incremental,
2086 // so we build a saved state file to pass.
Christopher Tate8e294d42011-08-31 20:37:12 -07002087 mSavedState = ParcelFileDescriptor.open(mSavedStateName,
Christopher Tate4a627c72011-04-01 14:43:32 -07002088 ParcelFileDescriptor.MODE_READ_ONLY |
2089 ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
Christopher Tatec7b31e32009-06-10 15:49:30 -07002090
Christopher Tate8e294d42011-08-31 20:37:12 -07002091 mBackupData = ParcelFileDescriptor.open(mBackupDataName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07002092 ParcelFileDescriptor.MODE_READ_WRITE |
2093 ParcelFileDescriptor.MODE_CREATE |
2094 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07002095
Christopher Tate8e294d42011-08-31 20:37:12 -07002096 mNewState = ParcelFileDescriptor.open(mNewStateName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07002097 ParcelFileDescriptor.MODE_READ_WRITE |
2098 ParcelFileDescriptor.MODE_CREATE |
2099 ParcelFileDescriptor.MODE_TRUNCATE);
Christopher Tatec7b31e32009-06-10 15:49:30 -07002100
Christopher Tate44a27902010-01-27 17:15:49 -08002101 // Initiate the target's backup pass
Christopher Tate6de74ff2012-01-17 15:20:32 -08002102 addBackupTrace("setting timeout");
Christopher Tate8e294d42011-08-31 20:37:12 -07002103 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
Christopher Tate6de74ff2012-01-17 15:20:32 -08002104 addBackupTrace("calling agent doBackup()");
Christopher Tate8e294d42011-08-31 20:37:12 -07002105 agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
Christopher Tatec7b31e32009-06-10 15:49:30 -07002106 } catch (Exception e) {
Christopher Tate8e294d42011-08-31 20:37:12 -07002107 Slog.e(TAG, "Error invoking for backup on " + packageName);
Christopher Tate6de74ff2012-01-17 15:20:32 -08002108 addBackupTrace("exception: " + e);
Christopher Tate8e294d42011-08-31 20:37:12 -07002109 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
2110 e.toString());
2111 agentErrorCleanup();
2112 return BackupConstants.AGENT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07002113 }
2114
Christopher Tate8e294d42011-08-31 20:37:12 -07002115 // At this point the agent is off and running. The next thing to happen will
2116 // either be a callback from the agent, at which point we'll process its data
2117 // for transport, or a timeout. Either way the next phase will happen in
2118 // response to the TimeoutHandler interface callbacks.
Christopher Tate6de74ff2012-01-17 15:20:32 -08002119 addBackupTrace("invoke success");
Christopher Tate8e294d42011-08-31 20:37:12 -07002120 return BackupConstants.TRANSPORT_OK;
2121 }
2122
2123 @Override
2124 public void operationComplete() {
2125 // Okay, the agent successfully reported back to us. Spin the data off to the
2126 // transport and proceed with the next stage.
2127 if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
2128 + mCurrentPackage.packageName);
2129 mBackupHandler.removeMessages(MSG_TIMEOUT);
2130 clearAgentState();
Christopher Tate6de74ff2012-01-17 15:20:32 -08002131 addBackupTrace("operation complete");
Christopher Tate8e294d42011-08-31 20:37:12 -07002132
2133 ParcelFileDescriptor backupData = null;
2134 mStatus = BackupConstants.TRANSPORT_OK;
Dan Egnorbb9001c2009-07-27 12:20:13 -07002135 try {
Christopher Tate8e294d42011-08-31 20:37:12 -07002136 int size = (int) mBackupDataName.length();
Dan Egnorbb9001c2009-07-27 12:20:13 -07002137 if (size > 0) {
Christopher Tate8e294d42011-08-31 20:37:12 -07002138 if (mStatus == BackupConstants.TRANSPORT_OK) {
2139 backupData = ParcelFileDescriptor.open(mBackupDataName,
Dan Egnor01445162009-09-21 17:04:05 -07002140 ParcelFileDescriptor.MODE_READ_ONLY);
Christopher Tate6de74ff2012-01-17 15:20:32 -08002141 addBackupTrace("sending data to transport");
Christopher Tate8e294d42011-08-31 20:37:12 -07002142 mStatus = mTransport.performBackup(mCurrentPackage, backupData);
Dan Egnor01445162009-09-21 17:04:05 -07002143 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07002144
Dan Egnor83861e72009-09-17 16:17:55 -07002145 // TODO - We call finishBackup() for each application backed up, because
2146 // we need to know now whether it succeeded or failed. Instead, we should
2147 // hold off on finishBackup() until the end, which implies holding off on
2148 // renaming *all* the output state files (see below) until that happens.
2149
Christopher Tate6de74ff2012-01-17 15:20:32 -08002150 addBackupTrace("data delivered: " + mStatus);
Christopher Tate8e294d42011-08-31 20:37:12 -07002151 if (mStatus == BackupConstants.TRANSPORT_OK) {
Christopher Tate6de74ff2012-01-17 15:20:32 -08002152 addBackupTrace("finishing op on transport");
Christopher Tate8e294d42011-08-31 20:37:12 -07002153 mStatus = mTransport.finishBackup();
Christopher Tate6de74ff2012-01-17 15:20:32 -08002154 addBackupTrace("finished: " + mStatus);
Dan Egnor83861e72009-09-17 16:17:55 -07002155 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07002156 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002157 if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
Christopher Tate6de74ff2012-01-17 15:20:32 -08002158 addBackupTrace("no data to send");
Dan Egnorbb9001c2009-07-27 12:20:13 -07002159 }
2160
2161 // After successful transport, delete the now-stale data
2162 // and juggle the files so that next time we supply the agent
2163 // with the new state file it just created.
Christopher Tate8e294d42011-08-31 20:37:12 -07002164 if (mStatus == BackupConstants.TRANSPORT_OK) {
2165 mBackupDataName.delete();
2166 mNewStateName.renameTo(mSavedStateName);
2167 EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE,
2168 mCurrentPackage.packageName, size);
2169 logBackupComplete(mCurrentPackage.packageName);
Dan Egnor01445162009-09-21 17:04:05 -07002170 } else {
Christopher Tate8e294d42011-08-31 20:37:12 -07002171 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
2172 mCurrentPackage.packageName);
Dan Egnor01445162009-09-21 17:04:05 -07002173 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07002174 } catch (Exception e) {
Christopher Tate8e294d42011-08-31 20:37:12 -07002175 Slog.e(TAG, "Transport error backing up " + mCurrentPackage.packageName, e);
2176 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
2177 mCurrentPackage.packageName);
2178 mStatus = BackupConstants.TRANSPORT_ERROR;
Dan Egnorbb9001c2009-07-27 12:20:13 -07002179 } finally {
2180 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
Christopher Tatec7b31e32009-06-10 15:49:30 -07002181 }
Christopher Tated55e18a2009-09-21 10:12:59 -07002182
Christopher Tate8e294d42011-08-31 20:37:12 -07002183 // If we encountered an error here it's a transport-level failure. That
2184 // means we need to halt everything and reschedule everything for next time.
2185 final BackupState nextState;
2186 if (mStatus != BackupConstants.TRANSPORT_OK) {
2187 revertAndEndBackup();
2188 nextState = BackupState.FINAL;
2189 } else {
2190 // Success! Proceed with the next app if any, otherwise we're done.
2191 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
2192 }
2193
2194 executeNextState(nextState);
2195 }
2196
2197 @Override
2198 public void handleTimeout() {
2199 // Whoops, the current agent timed out running doBackup(). Tidy up and restage
2200 // it for the next time we run a backup pass.
2201 // !!! TODO: keep track of failure counts per agent, and blacklist those which
2202 // fail repeatedly (i.e. have proved themselves to be buggy).
2203 Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
2204 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
2205 "timeout");
Christopher Tate6de74ff2012-01-17 15:20:32 -08002206 addBackupTrace("timeout of " + mCurrentPackage.packageName);
Christopher Tate8e294d42011-08-31 20:37:12 -07002207 agentErrorCleanup();
2208 dataChangedImpl(mCurrentPackage.packageName);
2209 }
2210
2211 void revertAndEndBackup() {
2212 if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
Christopher Tate6de74ff2012-01-17 15:20:32 -08002213 addBackupTrace("transport error; reverting");
Christopher Tate8e294d42011-08-31 20:37:12 -07002214 for (BackupRequest request : mOriginalQueue) {
2215 dataChangedImpl(request.packageName);
2216 }
2217 // We also want to reset the backup schedule based on whatever
2218 // the transport suggests by way of retry/backoff time.
2219 restartBackupAlarm();
2220 }
2221
2222 void agentErrorCleanup() {
2223 mBackupDataName.delete();
2224 mNewStateName.delete();
2225 clearAgentState();
2226
2227 executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
2228 }
2229
2230 // Cleanup common to both success and failure cases
2231 void clearAgentState() {
2232 try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
2233 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
2234 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
2235 mSavedState = mBackupData = mNewState = null;
2236 synchronized (mCurrentOpLock) {
2237 mCurrentOperations.clear();
2238 }
2239
2240 // If this was a pseudopackage there's no associated Activity Manager state
2241 if (mCurrentPackage.applicationInfo != null) {
Christopher Tate6de74ff2012-01-17 15:20:32 -08002242 addBackupTrace("unbinding " + mCurrentPackage.packageName);
Christopher Tate8e294d42011-08-31 20:37:12 -07002243 try { // unbind even on timeout, just in case
2244 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
2245 } catch (RemoteException e) {}
2246 }
2247 }
2248
2249 void restartBackupAlarm() {
Christopher Tate6de74ff2012-01-17 15:20:32 -08002250 addBackupTrace("setting backup trigger");
Christopher Tate8e294d42011-08-31 20:37:12 -07002251 synchronized (mQueueLock) {
2252 try {
2253 startBackupAlarmsLocked(mTransport.requestBackupTime());
2254 } catch (RemoteException e) { /* cannot happen */ }
2255 }
2256 }
2257
2258 void executeNextState(BackupState nextState) {
2259 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
2260 + this + " nextState=" + nextState);
Christopher Tate6de74ff2012-01-17 15:20:32 -08002261 addBackupTrace("executeNextState => " + nextState);
Christopher Tate8e294d42011-08-31 20:37:12 -07002262 mCurrentState = nextState;
2263 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
2264 mBackupHandler.sendMessage(msg);
Christopher Tatec7b31e32009-06-10 15:49:30 -07002265 }
Christopher Tate043dadc2009-06-02 16:11:00 -07002266 }
2267
Christopher Tatedf01dea2009-06-09 20:45:02 -07002268
Christopher Tate4a627c72011-04-01 14:43:32 -07002269 // ----- Full backup to a file/socket -----
2270
2271 class PerformFullBackupTask implements Runnable {
2272 ParcelFileDescriptor mOutputFile;
Christopher Tate7926a692011-07-11 11:31:57 -07002273 DeflaterOutputStream mDeflater;
Christopher Tate4a627c72011-04-01 14:43:32 -07002274 IFullBackupRestoreObserver mObserver;
2275 boolean mIncludeApks;
2276 boolean mIncludeShared;
2277 boolean mAllApps;
Christopher Tate240c7d22011-10-03 18:13:44 -07002278 final boolean mIncludeSystem;
Christopher Tate4a627c72011-04-01 14:43:32 -07002279 String[] mPackages;
Christopher Tate728a1c42011-07-28 18:03:03 -07002280 String mCurrentPassword;
2281 String mEncryptPassword;
Christopher Tate4a627c72011-04-01 14:43:32 -07002282 AtomicBoolean mLatchObject;
2283 File mFilesDir;
2284 File mManifestFile;
2285
Christopher Tate7926a692011-07-11 11:31:57 -07002286 class FullBackupRunner implements Runnable {
2287 PackageInfo mPackage;
2288 IBackupAgent mAgent;
2289 ParcelFileDescriptor mPipe;
2290 int mToken;
2291 boolean mSendApk;
Christopher Tate73d73692012-01-20 17:11:31 -08002292 boolean mWriteManifest;
Christopher Tate7926a692011-07-11 11:31:57 -07002293
2294 FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
Christopher Tate73d73692012-01-20 17:11:31 -08002295 int token, boolean sendApk, boolean writeManifest) throws IOException {
Christopher Tate7926a692011-07-11 11:31:57 -07002296 mPackage = pack;
2297 mAgent = agent;
2298 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
2299 mToken = token;
2300 mSendApk = sendApk;
Christopher Tate73d73692012-01-20 17:11:31 -08002301 mWriteManifest = writeManifest;
Christopher Tate7926a692011-07-11 11:31:57 -07002302 }
2303
2304 @Override
2305 public void run() {
2306 try {
2307 BackupDataOutput output = new BackupDataOutput(
2308 mPipe.getFileDescriptor());
2309
Christopher Tate73d73692012-01-20 17:11:31 -08002310 if (mWriteManifest) {
2311 if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
2312 writeAppManifest(mPackage, mManifestFile, mSendApk);
2313 FullBackup.backupToTar(mPackage.packageName, null, null,
2314 mFilesDir.getAbsolutePath(),
2315 mManifestFile.getAbsolutePath(),
2316 output);
2317 }
Christopher Tate7926a692011-07-11 11:31:57 -07002318
2319 if (mSendApk) {
2320 writeApkToBackup(mPackage, output);
2321 }
2322
Christopher Tatec58efa62011-08-01 19:20:14 -07002323 if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
Christopher Tate8e294d42011-08-31 20:37:12 -07002324 prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null);
Christopher Tate7926a692011-07-11 11:31:57 -07002325 mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
2326 } catch (IOException e) {
2327 Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
2328 } catch (RemoteException e) {
2329 Slog.e(TAG, "Remote agent vanished during full backup of "
2330 + mPackage.packageName);
2331 } finally {
2332 try {
2333 mPipe.close();
2334 } catch (IOException e) {}
2335 }
2336 }
2337 }
2338
Christopher Tate4a627c72011-04-01 14:43:32 -07002339 PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
Christopher Tate728a1c42011-07-28 18:03:03 -07002340 boolean includeApks, boolean includeShared, String curPassword,
Christopher Tate240c7d22011-10-03 18:13:44 -07002341 String encryptPassword, boolean doAllApps, boolean doSystem, String[] packages,
Christopher Tate728a1c42011-07-28 18:03:03 -07002342 AtomicBoolean latch) {
Christopher Tate4a627c72011-04-01 14:43:32 -07002343 mOutputFile = fd;
2344 mObserver = observer;
2345 mIncludeApks = includeApks;
2346 mIncludeShared = includeShared;
2347 mAllApps = doAllApps;
Christopher Tate240c7d22011-10-03 18:13:44 -07002348 mIncludeSystem = doSystem;
Christopher Tate4a627c72011-04-01 14:43:32 -07002349 mPackages = packages;
Christopher Tate728a1c42011-07-28 18:03:03 -07002350 mCurrentPassword = curPassword;
2351 // when backing up, if there is a current backup password, we require that
2352 // the user use a nonempty encryption password as well. if one is supplied
2353 // in the UI we use that, but if the UI was left empty we fall back to the
2354 // current backup password (which was supplied by the user as well).
2355 if (encryptPassword == null || "".equals(encryptPassword)) {
2356 mEncryptPassword = curPassword;
2357 } else {
2358 mEncryptPassword = encryptPassword;
2359 }
Christopher Tate4a627c72011-04-01 14:43:32 -07002360 mLatchObject = latch;
2361
2362 mFilesDir = new File("/data/system");
2363 mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
2364 }
2365
2366 @Override
2367 public void run() {
Christopher Tate240c7d22011-10-03 18:13:44 -07002368 List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>();
Christopher Tate4a627c72011-04-01 14:43:32 -07002369
Christopher Tateb0628bf2011-06-02 15:08:13 -07002370 Slog.i(TAG, "--- Performing full-dataset backup ---");
Christopher Tate4a627c72011-04-01 14:43:32 -07002371 sendStartBackup();
2372
2373 // doAllApps supersedes the package set if any
2374 if (mAllApps) {
2375 packagesToBackup = mPackageManager.getInstalledPackages(
2376 PackageManager.GET_SIGNATURES);
Christopher Tate240c7d22011-10-03 18:13:44 -07002377 // Exclude system apps if we've been asked to do so
2378 if (mIncludeSystem == false) {
2379 for (int i = 0; i < packagesToBackup.size(); ) {
2380 PackageInfo pkg = packagesToBackup.get(i);
2381 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
2382 packagesToBackup.remove(i);
2383 } else {
2384 i++;
2385 }
2386 }
2387 }
2388 }
2389
2390 // Now process the command line argument packages, if any. Note that explicitly-
2391 // named system-partition packages will be included even if includeSystem was
2392 // set to false.
2393 if (mPackages != null) {
Christopher Tate4a627c72011-04-01 14:43:32 -07002394 for (String pkgName : mPackages) {
2395 try {
2396 packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
2397 PackageManager.GET_SIGNATURES));
2398 } catch (NameNotFoundException e) {
2399 Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
2400 }
2401 }
2402 }
2403
Christopher Tate73d73692012-01-20 17:11:31 -08002404 // Cull any packages that have indicated that backups are not permitted, as well
2405 // as any explicit mention of the 'special' shared-storage agent package (we
2406 // handle that one at the end).
Christopher Tatea858cb02011-06-03 12:27:51 -07002407 for (int i = 0; i < packagesToBackup.size(); ) {
Christopher Tate240c7d22011-10-03 18:13:44 -07002408 PackageInfo pkg = packagesToBackup.get(i);
Christopher Tate73d73692012-01-20 17:11:31 -08002409 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0
2410 || pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) {
Christopher Tatea858cb02011-06-03 12:27:51 -07002411 packagesToBackup.remove(i);
2412 } else {
2413 i++;
2414 }
2415 }
2416
Christopher Tate7926a692011-07-11 11:31:57 -07002417 FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
Christopher Tate2efd2db2011-07-19 16:32:49 -07002418 OutputStream out = null;
Christopher Tate7926a692011-07-11 11:31:57 -07002419
Christopher Tate4a627c72011-04-01 14:43:32 -07002420 PackageInfo pkg = null;
2421 try {
Christopher Tate728a1c42011-07-28 18:03:03 -07002422 boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
Christopher Tate2efd2db2011-07-19 16:32:49 -07002423 boolean compressing = COMPRESS_FULL_BACKUPS;
2424 OutputStream finalOutput = ofstream;
Christopher Tate7bdb0962011-07-13 19:30:21 -07002425
Christopher Tateeef4ae42011-08-05 13:15:53 -07002426 // Verify that the given password matches the currently-active
2427 // backup password, if any
2428 if (hasBackupPassword()) {
2429 if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) {
2430 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
2431 return;
2432 }
2433 }
2434
Christopher Tate7bdb0962011-07-13 19:30:21 -07002435 // Write the global file header. All strings are UTF-8 encoded; lines end
2436 // with a '\n' byte. Actual backup data begins immediately following the
2437 // final '\n'.
2438 //
2439 // line 1: "ANDROID BACKUP"
2440 // line 2: backup file format version, currently "1"
2441 // line 3: compressed? "0" if not compressed, "1" if compressed.
Christopher Tate2efd2db2011-07-19 16:32:49 -07002442 // line 4: name of encryption algorithm [currently only "none" or "AES-256"]
2443 //
2444 // When line 4 is not "none", then additional header data follows:
2445 //
2446 // line 5: user password salt [hex]
2447 // line 6: master key checksum salt [hex]
2448 // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal]
2449 // line 8: IV of the user key [hex]
2450 // line 9: master key blob [hex]
2451 // IV of the master key, master key itself, master key checksum hash
2452 //
2453 // The master key checksum is the master key plus its checksum salt, run through
2454 // 10k rounds of PBKDF2. This is used to verify that the user has supplied the
2455 // correct password for decrypting the archive: the master key decrypted from
2456 // the archive using the user-supplied password is also run through PBKDF2 in
2457 // this way, and if the result does not match the checksum as stored in the
2458 // archive, then we know that the user-supplied password does not match the
2459 // archive's.
2460 StringBuilder headerbuf = new StringBuilder(1024);
2461
Christopher Tate7bdb0962011-07-13 19:30:21 -07002462 headerbuf.append(BACKUP_FILE_HEADER_MAGIC);
Christopher Tate2efd2db2011-07-19 16:32:49 -07002463 headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n
2464 headerbuf.append(compressing ? "\n1\n" : "\n0\n");
Christopher Tate7bdb0962011-07-13 19:30:21 -07002465
2466 try {
Christopher Tate2efd2db2011-07-19 16:32:49 -07002467 // Set up the encryption stage if appropriate, and emit the correct header
2468 if (encrypting) {
Christopher Tate2efd2db2011-07-19 16:32:49 -07002469 finalOutput = emitAesBackupHeader(headerbuf, finalOutput);
2470 } else {
2471 headerbuf.append("none\n");
2472 }
2473
Christopher Tate7bdb0962011-07-13 19:30:21 -07002474 byte[] header = headerbuf.toString().getBytes("UTF-8");
2475 ofstream.write(header);
Christopher Tate2efd2db2011-07-19 16:32:49 -07002476
2477 // Set up the compression stage feeding into the encryption stage (if any)
2478 if (compressing) {
2479 Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
2480 finalOutput = new DeflaterOutputStream(finalOutput, deflater, true);
2481 }
2482
2483 out = finalOutput;
Christopher Tate7bdb0962011-07-13 19:30:21 -07002484 } catch (Exception e) {
2485 // Should never happen!
2486 Slog.e(TAG, "Unable to emit archive header", e);
2487 return;
2488 }
2489
Christopher Tate73d73692012-01-20 17:11:31 -08002490 // Shared storage if requested
2491 if (mIncludeShared) {
2492 try {
2493 pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
2494 packagesToBackup.add(pkg);
2495 } catch (NameNotFoundException e) {
2496 Slog.e(TAG, "Unable to find shared-storage backup handler");
2497 }
2498 }
2499
Christopher Tateb0628bf2011-06-02 15:08:13 -07002500 // Now back up the app data via the agent mechanism
Christopher Tate4a627c72011-04-01 14:43:32 -07002501 int N = packagesToBackup.size();
2502 for (int i = 0; i < N; i++) {
2503 pkg = packagesToBackup.get(i);
Christopher Tate7926a692011-07-11 11:31:57 -07002504 backupOnePackage(pkg, out);
Christopher Tateb0628bf2011-06-02 15:08:13 -07002505 }
Christopher Tate4a627c72011-04-01 14:43:32 -07002506
Christopher Tate6853fcf2011-08-10 17:52:21 -07002507 // Done!
2508 finalizeBackup(out);
Christopher Tate4a627c72011-04-01 14:43:32 -07002509 } catch (RemoteException e) {
2510 Slog.e(TAG, "App died during full backup");
Christopher Tateaa0c02d2012-03-23 13:56:34 -07002511 } catch (Exception e) {
2512 Slog.e(TAG, "Internal exception during full backup", e);
Christopher Tate4a627c72011-04-01 14:43:32 -07002513 } finally {
Christopher Tateb0628bf2011-06-02 15:08:13 -07002514 tearDown(pkg);
Christopher Tate4a627c72011-04-01 14:43:32 -07002515 try {
Christopher Tate2efd2db2011-07-19 16:32:49 -07002516 if (out != null) out.close();
Christopher Tate4a627c72011-04-01 14:43:32 -07002517 mOutputFile.close();
2518 } catch (IOException e) {
2519 /* nothing we can do about this */
2520 }
2521 synchronized (mCurrentOpLock) {
2522 mCurrentOperations.clear();
2523 }
2524 synchronized (mLatchObject) {
2525 mLatchObject.set(true);
2526 mLatchObject.notifyAll();
2527 }
2528 sendEndBackup();
Christopher Tate4a627c72011-04-01 14:43:32 -07002529 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
Christopher Tate336a6492011-10-05 16:05:43 -07002530 mWakelock.release();
Christopher Tate4a627c72011-04-01 14:43:32 -07002531 }
2532 }
2533
Christopher Tate2efd2db2011-07-19 16:32:49 -07002534 private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
2535 OutputStream ofstream) throws Exception {
2536 // User key will be used to encrypt the master key.
2537 byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
Christopher Tate728a1c42011-07-28 18:03:03 -07002538 SecretKey userKey = buildPasswordKey(mEncryptPassword, newUserSalt,
Christopher Tate2efd2db2011-07-19 16:32:49 -07002539 PBKDF2_HASH_ROUNDS);
2540
2541 // the master key is random for each backup
2542 byte[] masterPw = new byte[256 / 8];
2543 mRng.nextBytes(masterPw);
2544 byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE);
2545
2546 // primary encryption of the datastream with the random key
2547 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
2548 SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES");
2549 c.init(Cipher.ENCRYPT_MODE, masterKeySpec);
2550 OutputStream finalOutput = new CipherOutputStream(ofstream, c);
2551
2552 // line 4: name of encryption algorithm
2553 headerbuf.append(ENCRYPTION_ALGORITHM_NAME);
2554 headerbuf.append('\n');
2555 // line 5: user password salt [hex]
2556 headerbuf.append(byteArrayToHex(newUserSalt));
2557 headerbuf.append('\n');
2558 // line 6: master key checksum salt [hex]
2559 headerbuf.append(byteArrayToHex(checksumSalt));
2560 headerbuf.append('\n');
2561 // line 7: number of PBKDF2 rounds used [decimal]
2562 headerbuf.append(PBKDF2_HASH_ROUNDS);
2563 headerbuf.append('\n');
2564
2565 // line 8: IV of the user key [hex]
2566 Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding");
2567 mkC.init(Cipher.ENCRYPT_MODE, userKey);
2568
2569 byte[] IV = mkC.getIV();
2570 headerbuf.append(byteArrayToHex(IV));
2571 headerbuf.append('\n');
2572
2573 // line 9: master IV + key blob, encrypted by the user key [hex]. Blob format:
2574 // [byte] IV length = Niv
2575 // [array of Niv bytes] IV itself
2576 // [byte] master key length = Nmk
2577 // [array of Nmk bytes] master key itself
2578 // [byte] MK checksum hash length = Nck
2579 // [array of Nck bytes] master key checksum hash
2580 //
2581 // The checksum is the (master key + checksum salt), run through the
2582 // stated number of PBKDF2 rounds
2583 IV = c.getIV();
2584 byte[] mk = masterKeySpec.getEncoded();
2585 byte[] checksum = makeKeyChecksum(masterKeySpec.getEncoded(),
2586 checksumSalt, PBKDF2_HASH_ROUNDS);
2587
2588 ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
2589 + checksum.length + 3);
2590 DataOutputStream mkOut = new DataOutputStream(blob);
2591 mkOut.writeByte(IV.length);
2592 mkOut.write(IV);
2593 mkOut.writeByte(mk.length);
2594 mkOut.write(mk);
2595 mkOut.writeByte(checksum.length);
2596 mkOut.write(checksum);
2597 mkOut.flush();
2598 byte[] encryptedMk = mkC.doFinal(blob.toByteArray());
2599 headerbuf.append(byteArrayToHex(encryptedMk));
2600 headerbuf.append('\n');
2601
2602 return finalOutput;
2603 }
2604
2605 private void backupOnePackage(PackageInfo pkg, OutputStream out)
Christopher Tate7926a692011-07-11 11:31:57 -07002606 throws RemoteException {
Christopher Tateb0628bf2011-06-02 15:08:13 -07002607 Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
2608
2609 IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
2610 IApplicationThread.BACKUP_MODE_FULL);
2611 if (agent != null) {
Christopher Tate7926a692011-07-11 11:31:57 -07002612 ParcelFileDescriptor[] pipes = null;
Christopher Tateb0628bf2011-06-02 15:08:13 -07002613 try {
Christopher Tate73d73692012-01-20 17:11:31 -08002614 pipes = ParcelFileDescriptor.createPipe();
Christopher Tate7926a692011-07-11 11:31:57 -07002615
Christopher Tateb0628bf2011-06-02 15:08:13 -07002616 ApplicationInfo app = pkg.applicationInfo;
Christopher Tate73d73692012-01-20 17:11:31 -08002617 final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
Christopher Tate79ec80d2011-06-24 14:58:49 -07002618 final boolean sendApk = mIncludeApks
Christopher Tate73d73692012-01-20 17:11:31 -08002619 && !isSharedStorage
Christopher Tateb0628bf2011-06-02 15:08:13 -07002620 && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
2621 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
2622 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
2623
Christopher Tate73d73692012-01-20 17:11:31 -08002624 sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
Christopher Tateb0628bf2011-06-02 15:08:13 -07002625
Christopher Tate7926a692011-07-11 11:31:57 -07002626 final int token = generateToken();
2627 FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
Christopher Tate73d73692012-01-20 17:11:31 -08002628 token, sendApk, !isSharedStorage);
Christopher Tate7926a692011-07-11 11:31:57 -07002629 pipes[1].close(); // the runner has dup'd it
2630 pipes[1] = null;
2631 Thread t = new Thread(runner);
2632 t.start();
Christopher Tateb0628bf2011-06-02 15:08:13 -07002633
Christopher Tate7926a692011-07-11 11:31:57 -07002634 // Now pull data from the app and stuff it into the compressor
2635 try {
2636 FileInputStream raw = new FileInputStream(pipes[0].getFileDescriptor());
2637 DataInputStream in = new DataInputStream(raw);
Christopher Tate79ec80d2011-06-24 14:58:49 -07002638
Christopher Tate7926a692011-07-11 11:31:57 -07002639 byte[] buffer = new byte[16 * 1024];
2640 int chunkTotal;
2641 while ((chunkTotal = in.readInt()) > 0) {
2642 while (chunkTotal > 0) {
2643 int toRead = (chunkTotal > buffer.length)
2644 ? buffer.length : chunkTotal;
2645 int nRead = in.read(buffer, 0, toRead);
2646 out.write(buffer, 0, nRead);
2647 chunkTotal -= nRead;
2648 }
2649 }
2650 } catch (IOException e) {
2651 Slog.i(TAG, "Caught exception reading from agent", e);
Christopher Tateb0628bf2011-06-02 15:08:13 -07002652 }
2653
Christopher Tateb0628bf2011-06-02 15:08:13 -07002654 if (!waitUntilOperationComplete(token)) {
2655 Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
2656 } else {
Christopher Tate7926a692011-07-11 11:31:57 -07002657 if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
Christopher Tateb0628bf2011-06-02 15:08:13 -07002658 }
Christopher Tate7926a692011-07-11 11:31:57 -07002659
Christopher Tateb0628bf2011-06-02 15:08:13 -07002660 } catch (IOException e) {
2661 Slog.e(TAG, "Error backing up " + pkg.packageName, e);
Christopher Tate7926a692011-07-11 11:31:57 -07002662 } finally {
2663 try {
Christopher Tate2efd2db2011-07-19 16:32:49 -07002664 // flush after every package
2665 out.flush();
Christopher Tate7926a692011-07-11 11:31:57 -07002666 if (pipes != null) {
2667 if (pipes[0] != null) pipes[0].close();
2668 if (pipes[1] != null) pipes[1].close();
2669 }
Christopher Tate7926a692011-07-11 11:31:57 -07002670 } catch (IOException e) {
2671 Slog.w(TAG, "Error bringing down backup stack");
2672 }
Christopher Tateb0628bf2011-06-02 15:08:13 -07002673 }
2674 } else {
2675 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
2676 }
2677 tearDown(pkg);
2678 }
2679
Christopher Tate79ec80d2011-06-24 14:58:49 -07002680 private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
2681 // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
2682 final String appSourceDir = pkg.applicationInfo.sourceDir;
2683 final String apkDir = new File(appSourceDir).getParent();
2684 FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
2685 apkDir, appSourceDir, output);
2686
2687 // Save associated .obb content if it exists and we did save the apk
2688 // check for .obb and save those too
2689 final File obbDir = Environment.getExternalStorageAppObbDirectory(pkg.packageName);
2690 if (obbDir != null) {
Christopher Tatec58efa62011-08-01 19:20:14 -07002691 if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
Christopher Tate79ec80d2011-06-24 14:58:49 -07002692 File[] obbFiles = obbDir.listFiles();
2693 if (obbFiles != null) {
2694 final String obbDirName = obbDir.getAbsolutePath();
2695 for (File obb : obbFiles) {
2696 FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
2697 obbDirName, obb.getAbsolutePath(), output);
2698 }
2699 }
2700 }
2701 }
2702
Christopher Tate6853fcf2011-08-10 17:52:21 -07002703 private void finalizeBackup(OutputStream out) {
2704 try {
2705 // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes.
2706 byte[] eof = new byte[512 * 2]; // newly allocated == zero filled
2707 out.write(eof);
2708 } catch (IOException e) {
2709 Slog.w(TAG, "Error attempting to finalize backup stream");
2710 }
2711 }
2712
Christopher Tate4a627c72011-04-01 14:43:32 -07002713 private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
2714 throws IOException {
2715 // Manifest format. All data are strings ending in LF:
2716 // BACKUP_MANIFEST_VERSION, currently 1
2717 //
2718 // Version 1:
2719 // package name
2720 // package's versionCode
Christopher Tate75a99702011-05-18 16:28:19 -07002721 // platform versionCode
2722 // getInstallerPackageName() for this package (maybe empty)
2723 // boolean: "1" if archive includes .apk; any other string means not
Christopher Tate4a627c72011-04-01 14:43:32 -07002724 // number of signatures == N
2725 // N*: signature byte array in ascii format per Signature.toCharsString()
2726 StringBuilder builder = new StringBuilder(4096);
2727 StringBuilderPrinter printer = new StringBuilderPrinter(builder);
2728
2729 printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
2730 printer.println(pkg.packageName);
2731 printer.println(Integer.toString(pkg.versionCode));
Christopher Tate75a99702011-05-18 16:28:19 -07002732 printer.println(Integer.toString(Build.VERSION.SDK_INT));
2733
2734 String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
2735 printer.println((installerName != null) ? installerName : "");
2736
Christopher Tate4a627c72011-04-01 14:43:32 -07002737 printer.println(withApk ? "1" : "0");
2738 if (pkg.signatures == null) {
2739 printer.println("0");
2740 } else {
2741 printer.println(Integer.toString(pkg.signatures.length));
2742 for (Signature sig : pkg.signatures) {
2743 printer.println(sig.toCharsString());
2744 }
2745 }
2746
2747 FileOutputStream outstream = new FileOutputStream(manifestFile);
Christopher Tate4a627c72011-04-01 14:43:32 -07002748 outstream.write(builder.toString().getBytes());
2749 outstream.close();
2750 }
2751
2752 private void tearDown(PackageInfo pkg) {
Christopher Tateb0628bf2011-06-02 15:08:13 -07002753 if (pkg != null) {
2754 final ApplicationInfo app = pkg.applicationInfo;
2755 if (app != null) {
2756 try {
2757 // unbind and tidy up even on timeout or failure, just in case
2758 mActivityManager.unbindBackupAgent(app);
Christopher Tate4a627c72011-04-01 14:43:32 -07002759
Christopher Tateb0628bf2011-06-02 15:08:13 -07002760 // The agent was running with a stub Application object, so shut it down.
Christopher Tate2efd2db2011-07-19 16:32:49 -07002761 if (app.uid != Process.SYSTEM_UID
2762 && app.uid != Process.PHONE_UID) {
Christopher Tatec58efa62011-08-01 19:20:14 -07002763 if (MORE_DEBUG) Slog.d(TAG, "Backup complete, killing host process");
Christopher Tateb0628bf2011-06-02 15:08:13 -07002764 mActivityManager.killApplicationProcess(app.processName, app.uid);
2765 } else {
Christopher Tatec58efa62011-08-01 19:20:14 -07002766 if (MORE_DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
Christopher Tateb0628bf2011-06-02 15:08:13 -07002767 }
2768 } catch (RemoteException e) {
2769 Slog.d(TAG, "Lost app trying to shut down");
2770 }
Christopher Tate4a627c72011-04-01 14:43:32 -07002771 }
Christopher Tate4a627c72011-04-01 14:43:32 -07002772 }
2773 }
2774
2775 // wrappers for observer use
2776 void sendStartBackup() {
2777 if (mObserver != null) {
2778 try {
2779 mObserver.onStartBackup();
2780 } catch (RemoteException e) {
2781 Slog.w(TAG, "full backup observer went away: startBackup");
2782 mObserver = null;
2783 }
2784 }
2785 }
2786
2787 void sendOnBackupPackage(String name) {
2788 if (mObserver != null) {
2789 try {
2790 // TODO: use a more user-friendly name string
2791 mObserver.onBackupPackage(name);
2792 } catch (RemoteException e) {
2793 Slog.w(TAG, "full backup observer went away: backupPackage");
2794 mObserver = null;
2795 }
2796 }
2797 }
2798
2799 void sendEndBackup() {
2800 if (mObserver != null) {
2801 try {
2802 mObserver.onEndBackup();
2803 } catch (RemoteException e) {
2804 Slog.w(TAG, "full backup observer went away: endBackup");
2805 mObserver = null;
2806 }
2807 }
2808 }
2809 }
2810
2811
Christopher Tate75a99702011-05-18 16:28:19 -07002812 // ----- Full restore from a file/socket -----
2813
2814 // Description of a file in the restore datastream
2815 static class FileMetadata {
2816 String packageName; // name of the owning app
2817 String installerPackageName; // name of the market-type app that installed the owner
Christopher Tate79ec80d2011-06-24 14:58:49 -07002818 int type; // e.g. BackupAgent.TYPE_DIRECTORY
Christopher Tate75a99702011-05-18 16:28:19 -07002819 String domain; // e.g. FullBackup.DATABASE_TREE_TOKEN
2820 String path; // subpath within the semantic domain
2821 long mode; // e.g. 0666 (actually int)
2822 long mtime; // last mod time, UTC time_t (actually int)
2823 long size; // bytes of content
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002824
2825 @Override
2826 public String toString() {
2827 StringBuilder sb = new StringBuilder(128);
2828 sb.append("FileMetadata{");
2829 sb.append(packageName); sb.append(',');
2830 sb.append(type); sb.append(',');
2831 sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
2832 sb.append(size);
2833 sb.append('}');
2834 return sb.toString();
2835 }
Christopher Tate75a99702011-05-18 16:28:19 -07002836 }
2837
2838 enum RestorePolicy {
2839 IGNORE,
2840 ACCEPT,
2841 ACCEPT_IF_APK
2842 }
2843
2844 class PerformFullRestoreTask implements Runnable {
2845 ParcelFileDescriptor mInputFile;
Christopher Tate728a1c42011-07-28 18:03:03 -07002846 String mCurrentPassword;
2847 String mDecryptPassword;
Christopher Tate75a99702011-05-18 16:28:19 -07002848 IFullBackupRestoreObserver mObserver;
2849 AtomicBoolean mLatchObject;
2850 IBackupAgent mAgent;
2851 String mAgentPackage;
2852 ApplicationInfo mTargetApp;
2853 ParcelFileDescriptor[] mPipes = null;
2854
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002855 long mBytes;
2856
Christopher Tate75a99702011-05-18 16:28:19 -07002857 // possible handling states for a given package in the restore dataset
2858 final HashMap<String, RestorePolicy> mPackagePolicies
2859 = new HashMap<String, RestorePolicy>();
2860
2861 // installer package names for each encountered app, derived from the manifests
2862 final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
2863
2864 // Signatures for a given package found in its manifest file
2865 final HashMap<String, Signature[]> mManifestSignatures
2866 = new HashMap<String, Signature[]>();
2867
2868 // Packages we've already wiped data on when restoring their first file
2869 final HashSet<String> mClearedPackages = new HashSet<String>();
2870
Christopher Tate728a1c42011-07-28 18:03:03 -07002871 PerformFullRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
Christopher Tate2efd2db2011-07-19 16:32:49 -07002872 IFullBackupRestoreObserver observer, AtomicBoolean latch) {
Christopher Tate75a99702011-05-18 16:28:19 -07002873 mInputFile = fd;
Christopher Tate728a1c42011-07-28 18:03:03 -07002874 mCurrentPassword = curPassword;
2875 mDecryptPassword = decryptPassword;
Christopher Tate75a99702011-05-18 16:28:19 -07002876 mObserver = observer;
2877 mLatchObject = latch;
2878 mAgent = null;
2879 mAgentPackage = null;
2880 mTargetApp = null;
2881
2882 // Which packages we've already wiped data on. We prepopulate this
2883 // with a whitelist of packages known to be unclearable.
2884 mClearedPackages.add("android");
Christopher Tate75a99702011-05-18 16:28:19 -07002885 mClearedPackages.add("com.android.providers.settings");
Christopher Tateb0628bf2011-06-02 15:08:13 -07002886
Christopher Tate75a99702011-05-18 16:28:19 -07002887 }
2888
2889 class RestoreFileRunnable implements Runnable {
2890 IBackupAgent mAgent;
2891 FileMetadata mInfo;
2892 ParcelFileDescriptor mSocket;
2893 int mToken;
2894
2895 RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
2896 ParcelFileDescriptor socket, int token) throws IOException {
2897 mAgent = agent;
2898 mInfo = info;
2899 mToken = token;
2900
2901 // This class is used strictly for process-local binder invocations. The
2902 // semantics of ParcelFileDescriptor differ in this case; in particular, we
2903 // do not automatically get a 'dup'ed descriptor that we can can continue
2904 // to use asynchronously from the caller. So, we make sure to dup it ourselves
2905 // before proceeding to do the restore.
2906 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
2907 }
2908
2909 @Override
2910 public void run() {
2911 try {
2912 mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
2913 mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
2914 mToken, mBackupManagerBinder);
2915 } catch (RemoteException e) {
2916 // never happens; this is used strictly for local binder calls
2917 }
2918 }
2919 }
2920
2921 @Override
2922 public void run() {
2923 Slog.i(TAG, "--- Performing full-dataset restore ---");
2924 sendStartRestore();
2925
Christopher Tateb0628bf2011-06-02 15:08:13 -07002926 // Are we able to restore shared-storage data?
2927 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Christopher Tate73d73692012-01-20 17:11:31 -08002928 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
Christopher Tateb0628bf2011-06-02 15:08:13 -07002929 }
2930
Christopher Tate2efd2db2011-07-19 16:32:49 -07002931 FileInputStream rawInStream = null;
2932 DataInputStream rawDataIn = null;
Christopher Tate75a99702011-05-18 16:28:19 -07002933 try {
Christopher Tate728a1c42011-07-28 18:03:03 -07002934 if (hasBackupPassword()) {
2935 if (!passwordMatchesSaved(mCurrentPassword, PBKDF2_HASH_ROUNDS)) {
2936 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
2937 return;
2938 }
2939 }
2940
Christopher Tatee9e78ec2011-06-08 20:09:31 -07002941 mBytes = 0;
Christopher Tate75a99702011-05-18 16:28:19 -07002942 byte[] buffer = new byte[32 * 1024];
Christopher Tate2efd2db2011-07-19 16:32:49 -07002943 rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
2944 rawDataIn = new DataInputStream(rawInStream);
Christopher Tate7bdb0962011-07-13 19:30:21 -07002945
2946 // First, parse out the unencrypted/uncompressed header
2947 boolean compressed = false;
Christopher Tate2efd2db2011-07-19 16:32:49 -07002948 InputStream preCompressStream = rawInStream;
Christopher Tate7bdb0962011-07-13 19:30:21 -07002949 final InputStream in;
2950
2951 boolean okay = false;
2952 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
2953 byte[] streamHeader = new byte[headerLen];
Christopher Tate2efd2db2011-07-19 16:32:49 -07002954 rawDataIn.readFully(streamHeader);
2955 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
2956 if (Arrays.equals(magicBytes, streamHeader)) {
2957 // okay, header looks good. now parse out the rest of the fields.
2958 String s = readHeaderLine(rawInStream);
2959 if (Integer.parseInt(s) == BACKUP_FILE_VERSION) {
2960 // okay, it's a version we recognize
2961 s = readHeaderLine(rawInStream);
2962 compressed = (Integer.parseInt(s) != 0);
2963 s = readHeaderLine(rawInStream);
2964 if (s.equals("none")) {
2965 // no more header to parse; we're good to go
2966 okay = true;
Christopher Tate728a1c42011-07-28 18:03:03 -07002967 } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
Christopher Tate2efd2db2011-07-19 16:32:49 -07002968 preCompressStream = decodeAesHeaderAndInitialize(s, rawInStream);
2969 if (preCompressStream != null) {
Christopher Tate7bdb0962011-07-13 19:30:21 -07002970 okay = true;
Christopher Tate2efd2db2011-07-19 16:32:49 -07002971 }
2972 } else Slog.w(TAG, "Archive is encrypted but no password given");
2973 } else Slog.w(TAG, "Wrong header version: " + s);
2974 } else Slog.w(TAG, "Didn't read the right header magic");
Christopher Tate7bdb0962011-07-13 19:30:21 -07002975
2976 if (!okay) {
Christopher Tate2efd2db2011-07-19 16:32:49 -07002977 Slog.w(TAG, "Invalid restore data; aborting.");
Christopher Tate7bdb0962011-07-13 19:30:21 -07002978 return;
2979 }
2980
2981 // okay, use the right stream layer based on compression
Christopher Tate2efd2db2011-07-19 16:32:49 -07002982 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
Christopher Tate75a99702011-05-18 16:28:19 -07002983
2984 boolean didRestore;
2985 do {
Christopher Tate7926a692011-07-11 11:31:57 -07002986 didRestore = restoreOneFile(in, buffer);
Christopher Tate75a99702011-05-18 16:28:19 -07002987 } while (didRestore);
2988
Christopher Tatec58efa62011-08-01 19:20:14 -07002989 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
Christopher Tate7bdb0962011-07-13 19:30:21 -07002990 } catch (IOException e) {
2991 Slog.e(TAG, "Unable to read restore input");
Christopher Tate75a99702011-05-18 16:28:19 -07002992 } finally {
2993 tearDownPipes();
2994 tearDownAgent(mTargetApp);
2995
2996 try {
Christopher Tate2efd2db2011-07-19 16:32:49 -07002997 if (rawDataIn != null) rawDataIn.close();
2998 if (rawInStream != null) rawInStream.close();
Christopher Tate75a99702011-05-18 16:28:19 -07002999 mInputFile.close();
3000 } catch (IOException e) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07003001 Slog.w(TAG, "Close of restore data pipe threw", e);
Christopher Tate75a99702011-05-18 16:28:19 -07003002 /* nothing we can do about this */
3003 }
3004 synchronized (mCurrentOpLock) {
3005 mCurrentOperations.clear();
3006 }
3007 synchronized (mLatchObject) {
3008 mLatchObject.set(true);
3009 mLatchObject.notifyAll();
3010 }
3011 sendEndRestore();
Christopher Tatec58efa62011-08-01 19:20:14 -07003012 Slog.d(TAG, "Full restore pass complete.");
Christopher Tate336a6492011-10-05 16:05:43 -07003013 mWakelock.release();
Christopher Tate75a99702011-05-18 16:28:19 -07003014 }
3015 }
3016
Christopher Tate7bdb0962011-07-13 19:30:21 -07003017 String readHeaderLine(InputStream in) throws IOException {
3018 int c;
Christopher Tate2efd2db2011-07-19 16:32:49 -07003019 StringBuilder buffer = new StringBuilder(80);
Christopher Tate7bdb0962011-07-13 19:30:21 -07003020 while ((c = in.read()) >= 0) {
3021 if (c == '\n') break; // consume and discard the newlines
3022 buffer.append((char)c);
3023 }
3024 return buffer.toString();
3025 }
3026
Christopher Tate2efd2db2011-07-19 16:32:49 -07003027 InputStream decodeAesHeaderAndInitialize(String encryptionName, InputStream rawInStream) {
3028 InputStream result = null;
3029 try {
3030 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
3031
3032 String userSaltHex = readHeaderLine(rawInStream); // 5
3033 byte[] userSalt = hexToByteArray(userSaltHex);
3034
3035 String ckSaltHex = readHeaderLine(rawInStream); // 6
3036 byte[] ckSalt = hexToByteArray(ckSaltHex);
3037
3038 int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
3039 String userIvHex = readHeaderLine(rawInStream); // 8
3040
3041 String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
3042
3043 // decrypt the master key blob
3044 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
Christopher Tate728a1c42011-07-28 18:03:03 -07003045 SecretKey userKey = buildPasswordKey(mDecryptPassword, userSalt,
Christopher Tate2efd2db2011-07-19 16:32:49 -07003046 rounds);
3047 byte[] IV = hexToByteArray(userIvHex);
3048 IvParameterSpec ivSpec = new IvParameterSpec(IV);
3049 c.init(Cipher.DECRYPT_MODE,
3050 new SecretKeySpec(userKey.getEncoded(), "AES"),
3051 ivSpec);
3052 byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
3053 byte[] mkBlob = c.doFinal(mkCipher);
3054
3055 // first, the master key IV
3056 int offset = 0;
3057 int len = mkBlob[offset++];
3058 IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
3059 offset += len;
3060 // then the master key itself
3061 len = mkBlob[offset++];
3062 byte[] mk = Arrays.copyOfRange(mkBlob,
3063 offset, offset + len);
3064 offset += len;
3065 // and finally the master key checksum hash
3066 len = mkBlob[offset++];
3067 byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
3068 offset, offset + len);
3069
3070 // now validate the decrypted master key against the checksum
3071 byte[] calculatedCk = makeKeyChecksum(mk, ckSalt, rounds);
3072 if (Arrays.equals(calculatedCk, mkChecksum)) {
3073 ivSpec = new IvParameterSpec(IV);
3074 c.init(Cipher.DECRYPT_MODE,
3075 new SecretKeySpec(mk, "AES"),
3076 ivSpec);
3077 // Only if all of the above worked properly will 'result' be assigned
3078 result = new CipherInputStream(rawInStream, c);
3079 } else Slog.w(TAG, "Incorrect password");
3080 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
3081 } catch (InvalidAlgorithmParameterException e) {
3082 Slog.e(TAG, "Needed parameter spec unavailable!", e);
3083 } catch (BadPaddingException e) {
3084 // This case frequently occurs when the wrong password is used to decrypt
3085 // the master key. Use the identical "incorrect password" log text as is
3086 // used in the checksum failure log in order to avoid providing additional
3087 // information to an attacker.
3088 Slog.w(TAG, "Incorrect password");
3089 } catch (IllegalBlockSizeException e) {
3090 Slog.w(TAG, "Invalid block size in master key");
3091 } catch (NoSuchAlgorithmException e) {
3092 Slog.e(TAG, "Needed decryption algorithm unavailable!");
3093 } catch (NoSuchPaddingException e) {
3094 Slog.e(TAG, "Needed padding mechanism unavailable!");
3095 } catch (InvalidKeyException e) {
3096 Slog.w(TAG, "Illegal password; aborting");
3097 } catch (NumberFormatException e) {
3098 Slog.w(TAG, "Can't parse restore data header");
3099 } catch (IOException e) {
3100 Slog.w(TAG, "Can't read input header");
3101 }
3102
3103 return result;
3104 }
3105
Christopher Tate75a99702011-05-18 16:28:19 -07003106 boolean restoreOneFile(InputStream instream, byte[] buffer) {
3107 FileMetadata info;
3108 try {
3109 info = readTarHeaders(instream);
3110 if (info != null) {
Christopher Tatec58efa62011-08-01 19:20:14 -07003111 if (MORE_DEBUG) {
Christopher Tate75a99702011-05-18 16:28:19 -07003112 dumpFileMetadata(info);
3113 }
3114
3115 final String pkg = info.packageName;
3116 if (!pkg.equals(mAgentPackage)) {
3117 // okay, change in package; set up our various
3118 // bookkeeping if we haven't seen it yet
3119 if (!mPackagePolicies.containsKey(pkg)) {
3120 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3121 }
3122
3123 // Clean up the previous agent relationship if necessary,
3124 // and let the observer know we're considering a new app.
3125 if (mAgent != null) {
3126 if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
3127 tearDownPipes();
3128 tearDownAgent(mTargetApp);
3129 mTargetApp = null;
3130 mAgentPackage = null;
3131 }
3132 }
3133
3134 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
3135 mPackagePolicies.put(pkg, readAppManifest(info, instream));
3136 mPackageInstallers.put(pkg, info.installerPackageName);
3137 // We've read only the manifest content itself at this point,
3138 // so consume the footer before looping around to the next
3139 // input file
3140 skipTarPadding(info.size, instream);
3141 sendOnRestorePackage(pkg);
3142 } else {
3143 // Non-manifest, so it's actual file data. Is this a package
3144 // we're ignoring?
3145 boolean okay = true;
3146 RestorePolicy policy = mPackagePolicies.get(pkg);
3147 switch (policy) {
3148 case IGNORE:
3149 okay = false;
3150 break;
3151
3152 case ACCEPT_IF_APK:
3153 // If we're in accept-if-apk state, then the first file we
3154 // see MUST be the apk.
3155 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
3156 if (DEBUG) Slog.d(TAG, "APK file; installing");
3157 // Try to install the app.
3158 String installerName = mPackageInstallers.get(pkg);
3159 okay = installApk(info, installerName, instream);
3160 // good to go; promote to ACCEPT
3161 mPackagePolicies.put(pkg, (okay)
3162 ? RestorePolicy.ACCEPT
3163 : RestorePolicy.IGNORE);
3164 // At this point we've consumed this file entry
3165 // ourselves, so just strip the tar footer and
3166 // go on to the next file in the input stream
3167 skipTarPadding(info.size, instream);
3168 return true;
3169 } else {
3170 // File data before (or without) the apk. We can't
3171 // handle it coherently in this case so ignore it.
3172 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3173 okay = false;
3174 }
3175 break;
3176
3177 case ACCEPT:
3178 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
3179 if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
3180 // we can take the data without the apk, so we
3181 // *want* to do so. skip the apk by declaring this
3182 // one file not-okay without changing the restore
3183 // policy for the package.
3184 okay = false;
3185 }
3186 break;
3187
3188 default:
3189 // Something has gone dreadfully wrong when determining
3190 // the restore policy from the manifest. Ignore the
3191 // rest of this package's data.
3192 Slog.e(TAG, "Invalid policy from manifest");
3193 okay = false;
3194 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3195 break;
3196 }
3197
3198 // If the policy is satisfied, go ahead and set up to pipe the
3199 // data to the agent.
3200 if (DEBUG && okay && mAgent != null) {
3201 Slog.i(TAG, "Reusing existing agent instance");
3202 }
3203 if (okay && mAgent == null) {
3204 if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
3205
3206 try {
3207 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
3208
3209 // If we haven't sent any data to this app yet, we probably
3210 // need to clear it first. Check that.
3211 if (!mClearedPackages.contains(pkg)) {
Christopher Tate79ec80d2011-06-24 14:58:49 -07003212 // apps with their own backup agents are
Christopher Tate75a99702011-05-18 16:28:19 -07003213 // responsible for coherently managing a full
3214 // restore.
Christopher Tate79ec80d2011-06-24 14:58:49 -07003215 if (mTargetApp.backupAgentName == null) {
Christopher Tate75a99702011-05-18 16:28:19 -07003216 if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
3217 clearApplicationDataSynchronous(pkg);
3218 } else {
Christopher Tate79ec80d2011-06-24 14:58:49 -07003219 if (DEBUG) Slog.d(TAG, "backup agent ("
3220 + mTargetApp.backupAgentName + ") => no clear");
Christopher Tate75a99702011-05-18 16:28:19 -07003221 }
3222 mClearedPackages.add(pkg);
3223 } else {
3224 if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
3225 }
3226
3227 // All set; now set up the IPC and launch the agent
3228 setUpPipes();
3229 mAgent = bindToAgentSynchronous(mTargetApp,
3230 IApplicationThread.BACKUP_MODE_RESTORE_FULL);
3231 mAgentPackage = pkg;
3232 } catch (IOException e) {
3233 // fall through to error handling
3234 } catch (NameNotFoundException e) {
3235 // fall through to error handling
3236 }
3237
3238 if (mAgent == null) {
3239 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
3240 okay = false;
3241 tearDownPipes();
3242 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3243 }
3244 }
3245
3246 // Sanity check: make sure we never give data to the wrong app. This
3247 // should never happen but a little paranoia here won't go amiss.
3248 if (okay && !pkg.equals(mAgentPackage)) {
3249 Slog.e(TAG, "Restoring data for " + pkg
3250 + " but agent is for " + mAgentPackage);
3251 okay = false;
3252 }
3253
3254 // At this point we have an agent ready to handle the full
3255 // restore data as well as a pipe for sending data to
3256 // that agent. Tell the agent to start reading from the
3257 // pipe.
3258 if (okay) {
3259 boolean agentSuccess = true;
3260 long toCopy = info.size;
3261 final int token = generateToken();
3262 try {
3263 if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
3264 + info.path);
Christopher Tate8e294d42011-08-31 20:37:12 -07003265 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
Christopher Tate75a99702011-05-18 16:28:19 -07003266 // fire up the app's agent listening on the socket. If
3267 // the agent is running in the system process we can't
3268 // just invoke it asynchronously, so we provide a thread
3269 // for it here.
3270 if (mTargetApp.processName.equals("system")) {
3271 Slog.d(TAG, "system process agent - spinning a thread");
3272 RestoreFileRunnable runner = new RestoreFileRunnable(
3273 mAgent, info, mPipes[0], token);
3274 new Thread(runner).start();
3275 } else {
3276 mAgent.doRestoreFile(mPipes[0], info.size, info.type,
3277 info.domain, info.path, info.mode, info.mtime,
3278 token, mBackupManagerBinder);
3279 }
3280 } catch (IOException e) {
3281 // couldn't dup the socket for a process-local restore
3282 Slog.d(TAG, "Couldn't establish restore");
3283 agentSuccess = false;
3284 okay = false;
3285 } catch (RemoteException e) {
3286 // whoops, remote agent went away. We'll eat the content
3287 // ourselves, then, and not copy it over.
3288 Slog.e(TAG, "Agent crashed during full restore");
3289 agentSuccess = false;
3290 okay = false;
3291 }
3292
3293 // Copy over the data if the agent is still good
3294 if (okay) {
3295 boolean pipeOkay = true;
3296 FileOutputStream pipe = new FileOutputStream(
3297 mPipes[1].getFileDescriptor());
Christopher Tate75a99702011-05-18 16:28:19 -07003298 while (toCopy > 0) {
3299 int toRead = (toCopy > buffer.length)
3300 ? buffer.length : (int)toCopy;
3301 int nRead = instream.read(buffer, 0, toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07003302 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07003303 if (nRead <= 0) break;
3304 toCopy -= nRead;
3305
3306 // send it to the output pipe as long as things
3307 // are still good
3308 if (pipeOkay) {
3309 try {
3310 pipe.write(buffer, 0, nRead);
3311 } catch (IOException e) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07003312 Slog.e(TAG, "Failed to write to restore pipe", e);
Christopher Tate75a99702011-05-18 16:28:19 -07003313 pipeOkay = false;
3314 }
3315 }
3316 }
3317
3318 // done sending that file! Now we just need to consume
3319 // the delta from info.size to the end of block.
3320 skipTarPadding(info.size, instream);
3321
3322 // and now that we've sent it all, wait for the remote
3323 // side to acknowledge receipt
3324 agentSuccess = waitUntilOperationComplete(token);
3325 }
3326
3327 // okay, if the remote end failed at any point, deal with
3328 // it by ignoring the rest of the restore on it
3329 if (!agentSuccess) {
3330 mBackupHandler.removeMessages(MSG_TIMEOUT);
3331 tearDownPipes();
3332 tearDownAgent(mTargetApp);
3333 mAgent = null;
3334 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
3335 }
3336 }
3337
3338 // Problems setting up the agent communication, or an already-
3339 // ignored package: skip to the next tar stream entry by
3340 // reading and discarding this file.
3341 if (!okay) {
3342 if (DEBUG) Slog.d(TAG, "[discarding file content]");
3343 long bytesToConsume = (info.size + 511) & ~511;
3344 while (bytesToConsume > 0) {
3345 int toRead = (bytesToConsume > buffer.length)
3346 ? buffer.length : (int)bytesToConsume;
3347 long nRead = instream.read(buffer, 0, toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07003348 if (nRead >= 0) mBytes += nRead;
Christopher Tate75a99702011-05-18 16:28:19 -07003349 if (nRead <= 0) break;
3350 bytesToConsume -= nRead;
3351 }
3352 }
3353 }
3354 }
3355 } catch (IOException e) {
Christopher Tate2efd2db2011-07-19 16:32:49 -07003356 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
Christopher Tate75a99702011-05-18 16:28:19 -07003357 // treat as EOF
3358 info = null;
3359 }
3360
3361 return (info != null);
3362 }
3363
3364 void setUpPipes() throws IOException {
3365 mPipes = ParcelFileDescriptor.createPipe();
3366 }
3367
3368 void tearDownPipes() {
3369 if (mPipes != null) {
Christopher Tatee9e78ec2011-06-08 20:09:31 -07003370 try {
3371 mPipes[0].close();
3372 mPipes[0] = null;
3373 mPipes[1].close();
3374 mPipes[1] = null;
3375 } catch (IOException e) {
3376 Slog.w(TAG, "Couldn't close agent pipes", e);
Christopher Tate75a99702011-05-18 16:28:19 -07003377 }
3378 mPipes = null;
3379 }
3380 }
3381
3382 void tearDownAgent(ApplicationInfo app) {
3383 if (mAgent != null) {
3384 try {
3385 // unbind and tidy up even on timeout or failure, just in case
3386 mActivityManager.unbindBackupAgent(app);
3387
3388 // The agent was running with a stub Application object, so shut it down.
3389 // !!! We hardcode the confirmation UI's package name here rather than use a
3390 // manifest flag! TODO something less direct.
3391 if (app.uid != Process.SYSTEM_UID
3392 && !app.packageName.equals("com.android.backupconfirm")) {
3393 if (DEBUG) Slog.d(TAG, "Killing host process");
3394 mActivityManager.killApplicationProcess(app.processName, app.uid);
3395 } else {
3396 if (DEBUG) Slog.d(TAG, "Not killing after full restore");
3397 }
3398 } catch (RemoteException e) {
3399 Slog.d(TAG, "Lost app trying to shut down");
3400 }
3401 mAgent = null;
3402 }
3403 }
3404
3405 class RestoreInstallObserver extends IPackageInstallObserver.Stub {
3406 final AtomicBoolean mDone = new AtomicBoolean();
Christopher Tatea858cb02011-06-03 12:27:51 -07003407 String mPackageName;
Christopher Tate75a99702011-05-18 16:28:19 -07003408 int mResult;
3409
3410 public void reset() {
3411 synchronized (mDone) {
3412 mDone.set(false);
3413 }
3414 }
3415
3416 public void waitForCompletion() {
3417 synchronized (mDone) {
3418 while (mDone.get() == false) {
3419 try {
3420 mDone.wait();
3421 } catch (InterruptedException e) { }
3422 }
3423 }
3424 }
3425
3426 int getResult() {
3427 return mResult;
3428 }
3429
3430 @Override
3431 public void packageInstalled(String packageName, int returnCode)
3432 throws RemoteException {
3433 synchronized (mDone) {
3434 mResult = returnCode;
Christopher Tatea858cb02011-06-03 12:27:51 -07003435 mPackageName = packageName;
Christopher Tate75a99702011-05-18 16:28:19 -07003436 mDone.set(true);
3437 mDone.notifyAll();
3438 }
3439 }
3440 }
Christopher Tatea858cb02011-06-03 12:27:51 -07003441
3442 class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
3443 final AtomicBoolean mDone = new AtomicBoolean();
3444 int mResult;
3445
3446 public void reset() {
3447 synchronized (mDone) {
3448 mDone.set(false);
3449 }
3450 }
3451
3452 public void waitForCompletion() {
3453 synchronized (mDone) {
3454 while (mDone.get() == false) {
3455 try {
3456 mDone.wait();
3457 } catch (InterruptedException e) { }
3458 }
3459 }
3460 }
3461
3462 @Override
3463 public void packageDeleted(String packageName, int returnCode) throws RemoteException {
3464 synchronized (mDone) {
3465 mResult = returnCode;
3466 mDone.set(true);
3467 mDone.notifyAll();
3468 }
3469 }
3470 }
3471
Christopher Tate75a99702011-05-18 16:28:19 -07003472 final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
Christopher Tatea858cb02011-06-03 12:27:51 -07003473 final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
Christopher Tate75a99702011-05-18 16:28:19 -07003474
3475 boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
3476 boolean okay = true;
3477
3478 if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
3479
3480 // The file content is an .apk file. Copy it out to a staging location and
3481 // attempt to install it.
3482 File apkFile = new File(mDataDir, info.packageName);
3483 try {
3484 FileOutputStream apkStream = new FileOutputStream(apkFile);
3485 byte[] buffer = new byte[32 * 1024];
3486 long size = info.size;
3487 while (size > 0) {
3488 long toRead = (buffer.length < size) ? buffer.length : size;
3489 int didRead = instream.read(buffer, 0, (int)toRead);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07003490 if (didRead >= 0) mBytes += didRead;
Christopher Tate75a99702011-05-18 16:28:19 -07003491 apkStream.write(buffer, 0, didRead);
3492 size -= didRead;
3493 }
3494 apkStream.close();
3495
3496 // make sure the installer can read it
3497 apkFile.setReadable(true, false);
3498
3499 // Now install it
3500 Uri packageUri = Uri.fromFile(apkFile);
3501 mInstallObserver.reset();
3502 mPackageManager.installPackage(packageUri, mInstallObserver,
Christopher Tateab63aa82011-09-26 16:30:30 -07003503 PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
3504 installerPackage);
Christopher Tate75a99702011-05-18 16:28:19 -07003505 mInstallObserver.waitForCompletion();
3506
3507 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
3508 // The only time we continue to accept install of data even if the
3509 // apk install failed is if we had already determined that we could
3510 // accept the data regardless.
3511 if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
3512 okay = false;
3513 }
Christopher Tatea858cb02011-06-03 12:27:51 -07003514 } else {
3515 // Okay, the install succeeded. Make sure it was the right app.
3516 boolean uninstall = false;
3517 if (!mInstallObserver.mPackageName.equals(info.packageName)) {
3518 Slog.w(TAG, "Restore stream claimed to include apk for "
3519 + info.packageName + " but apk was really "
3520 + mInstallObserver.mPackageName);
3521 // delete the package we just put in place; it might be fraudulent
3522 okay = false;
3523 uninstall = true;
3524 } else {
3525 try {
3526 PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
3527 PackageManager.GET_SIGNATURES);
3528 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
3529 Slog.w(TAG, "Restore stream contains apk of package "
3530 + info.packageName + " but it disallows backup/restore");
3531 okay = false;
3532 } else {
3533 // So far so good -- do the signatures match the manifest?
3534 Signature[] sigs = mManifestSignatures.get(info.packageName);
3535 if (!signaturesMatch(sigs, pkg)) {
3536 Slog.w(TAG, "Installed app " + info.packageName
3537 + " signatures do not match restore manifest");
3538 okay = false;
3539 uninstall = true;
3540 }
3541 }
3542 } catch (NameNotFoundException e) {
3543 Slog.w(TAG, "Install of package " + info.packageName
3544 + " succeeded but now not found");
3545 okay = false;
3546 }
3547 }
3548
3549 // If we're not okay at this point, we need to delete the package
3550 // that we just installed.
3551 if (uninstall) {
3552 mDeleteObserver.reset();
3553 mPackageManager.deletePackage(mInstallObserver.mPackageName,
3554 mDeleteObserver, 0);
3555 mDeleteObserver.waitForCompletion();
3556 }
Christopher Tate75a99702011-05-18 16:28:19 -07003557 }
3558 } catch (IOException e) {
3559 Slog.e(TAG, "Unable to transcribe restored apk for install");
3560 okay = false;
3561 } finally {
3562 apkFile.delete();
3563 }
3564
3565 return okay;
3566 }
3567
3568 // Given an actual file content size, consume the post-content padding mandated
3569 // by the tar format.
3570 void skipTarPadding(long size, InputStream instream) throws IOException {
3571 long partial = (size + 512) % 512;
3572 if (partial > 0) {
Christopher Tate6853fcf2011-08-10 17:52:21 -07003573 final int needed = 512 - (int)partial;
3574 byte[] buffer = new byte[needed];
3575 if (readExactly(instream, buffer, 0, needed) == needed) {
3576 mBytes += needed;
3577 } else throw new IOException("Unexpected EOF in padding");
Christopher Tate75a99702011-05-18 16:28:19 -07003578 }
3579 }
3580
3581 // Returns a policy constant; takes a buffer arg to reduce memory churn
3582 RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
3583 throws IOException {
3584 // Fail on suspiciously large manifest files
3585 if (info.size > 64 * 1024) {
3586 throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
3587 }
Christopher Tate6853fcf2011-08-10 17:52:21 -07003588
Christopher Tate75a99702011-05-18 16:28:19 -07003589 byte[] buffer = new byte[(int) info.size];
Christopher Tate6853fcf2011-08-10 17:52:21 -07003590 if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
3591 mBytes += info.size;
3592 } else throw new IOException("Unexpected EOF in manifest");
Christopher Tate75a99702011-05-18 16:28:19 -07003593
3594 RestorePolicy policy = RestorePolicy.IGNORE;
3595 String[] str = new String[1];
3596 int offset = 0;
3597
3598 try {
3599 offset = extractLine(buffer, offset, str);
3600 int version = Integer.parseInt(str[0]);
3601 if (version == BACKUP_MANIFEST_VERSION) {
3602 offset = extractLine(buffer, offset, str);
3603 String manifestPackage = str[0];
3604 // TODO: handle <original-package>
3605 if (manifestPackage.equals(info.packageName)) {
3606 offset = extractLine(buffer, offset, str);
3607 version = Integer.parseInt(str[0]); // app version
3608 offset = extractLine(buffer, offset, str);
3609 int platformVersion = Integer.parseInt(str[0]);
3610 offset = extractLine(buffer, offset, str);
3611 info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
3612 offset = extractLine(buffer, offset, str);
3613 boolean hasApk = str[0].equals("1");
3614 offset = extractLine(buffer, offset, str);
3615 int numSigs = Integer.parseInt(str[0]);
Christopher Tate75a99702011-05-18 16:28:19 -07003616 if (numSigs > 0) {
Christopher Tatea858cb02011-06-03 12:27:51 -07003617 Signature[] sigs = new Signature[numSigs];
Christopher Tate75a99702011-05-18 16:28:19 -07003618 for (int i = 0; i < numSigs; i++) {
3619 offset = extractLine(buffer, offset, str);
3620 sigs[i] = new Signature(str[0]);
3621 }
Christopher Tatea858cb02011-06-03 12:27:51 -07003622 mManifestSignatures.put(info.packageName, sigs);
Christopher Tate75a99702011-05-18 16:28:19 -07003623
3624 // Okay, got the manifest info we need...
3625 try {
Christopher Tate75a99702011-05-18 16:28:19 -07003626 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
3627 info.packageName, PackageManager.GET_SIGNATURES);
Christopher Tatea858cb02011-06-03 12:27:51 -07003628 // Fall through to IGNORE if the app explicitly disallows backup
3629 final int flags = pkgInfo.applicationInfo.flags;
3630 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
3631 // Verify signatures against any installed version; if they
3632 // don't match, then we fall though and ignore the data. The
3633 // signatureMatch() method explicitly ignores the signature
3634 // check for packages installed on the system partition, because
3635 // such packages are signed with the platform cert instead of
3636 // the app developer's cert, so they're different on every
3637 // device.
3638 if (signaturesMatch(sigs, pkgInfo)) {
3639 if (pkgInfo.versionCode >= version) {
3640 Slog.i(TAG, "Sig + version match; taking data");
3641 policy = RestorePolicy.ACCEPT;
3642 } else {
3643 // The data is from a newer version of the app than
3644 // is presently installed. That means we can only
3645 // use it if the matching apk is also supplied.
3646 Slog.d(TAG, "Data version " + version
3647 + " is newer than installed version "
3648 + pkgInfo.versionCode + " - requiring apk");
3649 policy = RestorePolicy.ACCEPT_IF_APK;
3650 }
Christopher Tate75a99702011-05-18 16:28:19 -07003651 } else {
Christopher Tatea858cb02011-06-03 12:27:51 -07003652 Slog.w(TAG, "Restore manifest signatures do not match "
3653 + "installed application for " + info.packageName);
Christopher Tate75a99702011-05-18 16:28:19 -07003654 }
Christopher Tatea858cb02011-06-03 12:27:51 -07003655 } else {
3656 if (DEBUG) Slog.i(TAG, "Restore manifest from "
3657 + info.packageName + " but allowBackup=false");
Christopher Tate75a99702011-05-18 16:28:19 -07003658 }
3659 } catch (NameNotFoundException e) {
3660 // Okay, the target app isn't installed. We can process
3661 // the restore properly only if the dataset provides the
3662 // apk file and we can successfully install it.
3663 if (DEBUG) Slog.i(TAG, "Package " + info.packageName
3664 + " not installed; requiring apk in dataset");
3665 policy = RestorePolicy.ACCEPT_IF_APK;
3666 }
3667
3668 if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
3669 Slog.i(TAG, "Cannot restore package " + info.packageName
3670 + " without the matching .apk");
3671 }
3672 } else {
3673 Slog.i(TAG, "Missing signature on backed-up package "
3674 + info.packageName);
3675 }
3676 } else {
3677 Slog.i(TAG, "Expected package " + info.packageName
3678 + " but restore manifest claims " + manifestPackage);
3679 }
3680 } else {
3681 Slog.i(TAG, "Unknown restore manifest version " + version
3682 + " for package " + info.packageName);
3683 }
3684 } catch (NumberFormatException e) {
3685 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
Kenny Root11373412011-07-28 15:13:33 -07003686 } catch (IllegalArgumentException e) {
3687 Slog.w(TAG, e.getMessage());
Christopher Tate75a99702011-05-18 16:28:19 -07003688 }
3689
3690 return policy;
3691 }
3692
3693 // Builds a line from a byte buffer starting at 'offset', and returns
3694 // the index of the next unconsumed data in the buffer.
3695 int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
3696 final int end = buffer.length;
3697 if (offset >= end) throw new IOException("Incomplete data");
3698
3699 int pos;
3700 for (pos = offset; pos < end; pos++) {
3701 byte c = buffer[pos];
3702 // at LF we declare end of line, and return the next char as the
3703 // starting point for the next time through
3704 if (c == '\n') {
3705 break;
3706 }
3707 }
3708 outStr[0] = new String(buffer, offset, pos - offset);
3709 pos++; // may be pointing an extra byte past the end but that's okay
3710 return pos;
3711 }
3712
3713 void dumpFileMetadata(FileMetadata info) {
3714 if (DEBUG) {
3715 StringBuilder b = new StringBuilder(128);
3716
3717 // mode string
Christopher Tate79ec80d2011-06-24 14:58:49 -07003718 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
Christopher Tate75a99702011-05-18 16:28:19 -07003719 b.append(((info.mode & 0400) != 0) ? 'r' : '-');
3720 b.append(((info.mode & 0200) != 0) ? 'w' : '-');
3721 b.append(((info.mode & 0100) != 0) ? 'x' : '-');
3722 b.append(((info.mode & 0040) != 0) ? 'r' : '-');
3723 b.append(((info.mode & 0020) != 0) ? 'w' : '-');
3724 b.append(((info.mode & 0010) != 0) ? 'x' : '-');
3725 b.append(((info.mode & 0004) != 0) ? 'r' : '-');
3726 b.append(((info.mode & 0002) != 0) ? 'w' : '-');
3727 b.append(((info.mode & 0001) != 0) ? 'x' : '-');
3728 b.append(String.format(" %9d ", info.size));
3729
3730 Date stamp = new Date(info.mtime);
3731 b.append(new SimpleDateFormat("MMM dd kk:mm:ss ").format(stamp));
3732
3733 b.append(info.packageName);
3734 b.append(" :: ");
3735 b.append(info.domain);
3736 b.append(" :: ");
3737 b.append(info.path);
3738
3739 Slog.i(TAG, b.toString());
3740 }
3741 }
3742 // Consume a tar file header block [sequence] and accumulate the relevant metadata
3743 FileMetadata readTarHeaders(InputStream instream) throws IOException {
3744 byte[] block = new byte[512];
3745 FileMetadata info = null;
3746
3747 boolean gotHeader = readTarHeader(instream, block);
3748 if (gotHeader) {
Christopher Tate2efd2db2011-07-19 16:32:49 -07003749 try {
3750 // okay, presume we're okay, and extract the various metadata
3751 info = new FileMetadata();
3752 info.size = extractRadix(block, 124, 12, 8);
3753 info.mtime = extractRadix(block, 136, 12, 8);
3754 info.mode = extractRadix(block, 100, 8, 8);
Christopher Tate75a99702011-05-18 16:28:19 -07003755
Christopher Tate2efd2db2011-07-19 16:32:49 -07003756 info.path = extractString(block, 345, 155); // prefix
3757 String path = extractString(block, 0, 100);
3758 if (path.length() > 0) {
3759 if (info.path.length() > 0) info.path += '/';
3760 info.path += path;
Christopher Tate75a99702011-05-18 16:28:19 -07003761 }
Christopher Tate75a99702011-05-18 16:28:19 -07003762
Christopher Tate2efd2db2011-07-19 16:32:49 -07003763 // tar link indicator field: 1 byte at offset 156 in the header.
3764 int typeChar = block[156];
3765 if (typeChar == 'x') {
3766 // pax extended header, so we need to read that
3767 gotHeader = readPaxExtendedHeader(instream, info);
3768 if (gotHeader) {
3769 // and after a pax extended header comes another real header -- read
3770 // that to find the real file type
3771 gotHeader = readTarHeader(instream, block);
Christopher Tatee9e78ec2011-06-08 20:09:31 -07003772 }
Christopher Tate2efd2db2011-07-19 16:32:49 -07003773 if (!gotHeader) throw new IOException("Bad or missing pax header");
3774
3775 typeChar = block[156];
Christopher Tatee9e78ec2011-06-08 20:09:31 -07003776 }
Christopher Tate75a99702011-05-18 16:28:19 -07003777
Christopher Tate2efd2db2011-07-19 16:32:49 -07003778 switch (typeChar) {
3779 case '0': info.type = BackupAgent.TYPE_FILE; break;
3780 case '5': {
3781 info.type = BackupAgent.TYPE_DIRECTORY;
3782 if (info.size != 0) {
3783 Slog.w(TAG, "Directory entry with nonzero size in header");
3784 info.size = 0;
3785 }
3786 break;
Christopher Tate75a99702011-05-18 16:28:19 -07003787 }
Christopher Tate2efd2db2011-07-19 16:32:49 -07003788 case 0: {
3789 // presume EOF
3790 if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
3791 return null;
3792 }
3793 default: {
3794 Slog.e(TAG, "Unknown tar entity type: " + typeChar);
3795 throw new IOException("Unknown entity type " + typeChar);
3796 }
Christopher Tate75a99702011-05-18 16:28:19 -07003797 }
Christopher Tate2efd2db2011-07-19 16:32:49 -07003798
3799 // Parse out the path
3800 //
3801 // first: apps/shared/unrecognized
3802 if (FullBackup.SHARED_PREFIX.regionMatches(0,
3803 info.path, 0, FullBackup.SHARED_PREFIX.length())) {
3804 // File in shared storage. !!! TODO: implement this.
3805 info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
Christopher Tate73d73692012-01-20 17:11:31 -08003806 info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
Christopher Tate2efd2db2011-07-19 16:32:49 -07003807 info.domain = FullBackup.SHARED_STORAGE_TOKEN;
3808 if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
3809 } else if (FullBackup.APPS_PREFIX.regionMatches(0,
3810 info.path, 0, FullBackup.APPS_PREFIX.length())) {
3811 // App content! Parse out the package name and domain
3812
3813 // strip the apps/ prefix
3814 info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
3815
3816 // extract the package name
3817 int slash = info.path.indexOf('/');
3818 if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
3819 info.packageName = info.path.substring(0, slash);
3820 info.path = info.path.substring(slash+1);
3821
3822 // if it's a manifest we're done, otherwise parse out the domains
3823 if (!info.path.equals(BACKUP_MANIFEST_FILENAME)) {
3824 slash = info.path.indexOf('/');
3825 if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
3826 info.domain = info.path.substring(0, slash);
3827 // validate that it's one of the domains we understand
3828 if (!info.domain.equals(FullBackup.APK_TREE_TOKEN)
3829 && !info.domain.equals(FullBackup.DATA_TREE_TOKEN)
3830 && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN)
3831 && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN)
3832 && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)
3833 && !info.domain.equals(FullBackup.OBB_TREE_TOKEN)
3834 && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
3835 throw new IOException("Unrecognized domain " + info.domain);
3836 }
3837
3838 info.path = info.path.substring(slash + 1);
3839 }
3840 }
3841 } catch (IOException e) {
3842 if (DEBUG) {
Christopher Tate6853fcf2011-08-10 17:52:21 -07003843 Slog.e(TAG, "Parse error in header: " + e.getMessage());
Christopher Tate2efd2db2011-07-19 16:32:49 -07003844 HEXLOG(block);
3845 }
3846 throw e;
Christopher Tate75a99702011-05-18 16:28:19 -07003847 }
3848 }
3849 return info;
3850 }
3851
Christopher Tate2efd2db2011-07-19 16:32:49 -07003852 private void HEXLOG(byte[] block) {
3853 int offset = 0;
3854 int todo = block.length;
3855 StringBuilder buf = new StringBuilder(64);
3856 while (todo > 0) {
3857 buf.append(String.format("%04x ", offset));
3858 int numThisLine = (todo > 16) ? 16 : todo;
3859 for (int i = 0; i < numThisLine; i++) {
3860 buf.append(String.format("%02x ", block[offset+i]));
3861 }
3862 Slog.i("hexdump", buf.toString());
3863 buf.setLength(0);
3864 todo -= numThisLine;
3865 offset += numThisLine;
Christopher Tate75a99702011-05-18 16:28:19 -07003866 }
Christopher Tate2efd2db2011-07-19 16:32:49 -07003867 }
3868
Christopher Tate6853fcf2011-08-10 17:52:21 -07003869 // Read exactly the given number of bytes into a buffer at the stated offset.
3870 // Returns false if EOF is encountered before the requested number of bytes
3871 // could be read.
3872 int readExactly(InputStream in, byte[] buffer, int offset, int size)
3873 throws IOException {
3874 if (size <= 0) throw new IllegalArgumentException("size must be > 0");
3875
3876 int soFar = 0;
3877 while (soFar < size) {
3878 int nRead = in.read(buffer, offset + soFar, size - soFar);
3879 if (nRead <= 0) {
3880 if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
3881 break;
Christopher Tate2efd2db2011-07-19 16:32:49 -07003882 }
Christopher Tate6853fcf2011-08-10 17:52:21 -07003883 soFar += nRead;
Christopher Tate2efd2db2011-07-19 16:32:49 -07003884 }
Christopher Tate6853fcf2011-08-10 17:52:21 -07003885 return soFar;
3886 }
3887
3888 boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
3889 final int got = readExactly(instream, block, 0, 512);
3890 if (got == 0) return false; // Clean EOF
3891 if (got < 512) throw new IOException("Unable to read full block header");
3892 mBytes += 512;
3893 return true;
Christopher Tate75a99702011-05-18 16:28:19 -07003894 }
3895
3896 // overwrites 'info' fields based on the pax extended header
3897 boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
3898 throws IOException {
3899 // We should never see a pax extended header larger than this
3900 if (info.size > 32*1024) {
3901 Slog.w(TAG, "Suspiciously large pax header size " + info.size
3902 + " - aborting");
3903 throw new IOException("Sanity failure: pax header size " + info.size);
3904 }
3905
3906 // read whole blocks, not just the content size
3907 int numBlocks = (int)((info.size + 511) >> 9);
3908 byte[] data = new byte[numBlocks * 512];
Christopher Tate6853fcf2011-08-10 17:52:21 -07003909 if (readExactly(instream, data, 0, data.length) < data.length) {
3910 throw new IOException("Unable to read full pax header");
Christopher Tate75a99702011-05-18 16:28:19 -07003911 }
Christopher Tate6853fcf2011-08-10 17:52:21 -07003912 mBytes += data.length;
Christopher Tate75a99702011-05-18 16:28:19 -07003913
3914 final int contentSize = (int) info.size;
3915 int offset = 0;
3916 do {
3917 // extract the line at 'offset'
3918 int eol = offset+1;
3919 while (eol < contentSize && data[eol] != ' ') eol++;
3920 if (eol >= contentSize) {
3921 // error: we just hit EOD looking for the end of the size field
3922 throw new IOException("Invalid pax data");
3923 }
3924 // eol points to the space between the count and the key
3925 int linelen = (int) extractRadix(data, offset, eol - offset, 10);
3926 int key = eol + 1; // start of key=value
3927 eol = offset + linelen - 1; // trailing LF
3928 int value;
3929 for (value = key+1; data[value] != '=' && value <= eol; value++);
3930 if (value > eol) {
3931 throw new IOException("Invalid pax declaration");
3932 }
3933
3934 // pax requires that key/value strings be in UTF-8
3935 String keyStr = new String(data, key, value-key, "UTF-8");
3936 // -1 to strip the trailing LF
3937 String valStr = new String(data, value+1, eol-value-1, "UTF-8");
3938
3939 if ("path".equals(keyStr)) {
3940 info.path = valStr;
3941 } else if ("size".equals(keyStr)) {
3942 info.size = Long.parseLong(valStr);
3943 } else {
3944 if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
3945 }
3946
3947 offset += linelen;
3948 } while (offset < contentSize);
3949
3950 return true;
3951 }
3952
3953 long extractRadix(byte[] data, int offset, int maxChars, int radix)
3954 throws IOException {
3955 long value = 0;
3956 final int end = offset + maxChars;
3957 for (int i = offset; i < end; i++) {
3958 final byte b = data[i];
Christopher Tate3f6c77b2011-06-07 13:17:17 -07003959 // Numeric fields in tar can terminate with either NUL or SPC
Christopher Tate75a99702011-05-18 16:28:19 -07003960 if (b == 0 || b == ' ') break;
3961 if (b < '0' || b > ('0' + radix - 1)) {
Christopher Tate2efd2db2011-07-19 16:32:49 -07003962 throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
Christopher Tate75a99702011-05-18 16:28:19 -07003963 }
3964 value = radix * value + (b - '0');
3965 }
3966 return value;
3967 }
3968
3969 String extractString(byte[] data, int offset, int maxChars) throws IOException {
3970 final int end = offset + maxChars;
3971 int eos = offset;
Christopher Tate3f6c77b2011-06-07 13:17:17 -07003972 // tar string fields terminate early with a NUL
3973 while (eos < end && data[eos] != 0) eos++;
Christopher Tate75a99702011-05-18 16:28:19 -07003974 return new String(data, offset, eos-offset, "US-ASCII");
3975 }
3976
3977 void sendStartRestore() {
3978 if (mObserver != null) {
3979 try {
3980 mObserver.onStartRestore();
3981 } catch (RemoteException e) {
3982 Slog.w(TAG, "full restore observer went away: startRestore");
3983 mObserver = null;
3984 }
3985 }
3986 }
3987
3988 void sendOnRestorePackage(String name) {
3989 if (mObserver != null) {
3990 try {
3991 // TODO: use a more user-friendly name string
3992 mObserver.onRestorePackage(name);
3993 } catch (RemoteException e) {
3994 Slog.w(TAG, "full restore observer went away: restorePackage");
3995 mObserver = null;
3996 }
3997 }
3998 }
3999
4000 void sendEndRestore() {
4001 if (mObserver != null) {
4002 try {
4003 mObserver.onEndRestore();
4004 } catch (RemoteException e) {
4005 Slog.w(TAG, "full restore observer went away: endRestore");
4006 mObserver = null;
4007 }
4008 }
4009 }
4010 }
4011
Christopher Tatedf01dea2009-06-09 20:45:02 -07004012 // ----- Restore handling -----
4013
Christopher Tate78dd4a72009-11-04 11:49:08 -08004014 private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
4015 // If the target resides on the system partition, we allow it to restore
4016 // data from the like-named package in a restore set even if the signatures
4017 // do not match. (Unlike general applications, those flashed to the system
4018 // partition will be signed with the device's platform certificate, so on
4019 // different phones the same system app will have different signatures.)
4020 if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004021 if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
Christopher Tate78dd4a72009-11-04 11:49:08 -08004022 return true;
4023 }
4024
Christopher Tate20efdf6b2009-06-18 19:41:36 -07004025 // Allow unsigned apps, but not signed on one device and unsigned on the other
4026 // !!! TODO: is this the right policy?
Christopher Tate78dd4a72009-11-04 11:49:08 -08004027 Signature[] deviceSigs = target.signatures;
Christopher Tatec58efa62011-08-01 19:20:14 -07004028 if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
Christopher Tate6aa41f42009-06-19 14:14:22 -07004029 + " device=" + deviceSigs);
Christopher Tate20efdf6b2009-06-18 19:41:36 -07004030 if ((storedSigs == null || storedSigs.length == 0)
4031 && (deviceSigs == null || deviceSigs.length == 0)) {
4032 return true;
4033 }
4034 if (storedSigs == null || deviceSigs == null) {
4035 return false;
4036 }
4037
Christopher Tateabce4e82009-06-18 18:35:32 -07004038 // !!! TODO: this demands that every stored signature match one
4039 // that is present on device, and does not demand the converse.
4040 // Is this this right policy?
4041 int nStored = storedSigs.length;
4042 int nDevice = deviceSigs.length;
4043
4044 for (int i=0; i < nStored; i++) {
4045 boolean match = false;
4046 for (int j=0; j < nDevice; j++) {
4047 if (storedSigs[i].equals(deviceSigs[j])) {
4048 match = true;
4049 break;
4050 }
4051 }
4052 if (!match) {
4053 return false;
4054 }
4055 }
4056 return true;
4057 }
4058
Christopher Tate2982d062011-09-06 20:35:24 -07004059 enum RestoreState {
4060 INITIAL,
4061 DOWNLOAD_DATA,
4062 PM_METADATA,
4063 RUNNING_QUEUE,
4064 FINAL
4065 }
4066
4067 class PerformRestoreTask implements BackupRestoreTask {
Christopher Tatedf01dea2009-06-09 20:45:02 -07004068 private IBackupTransport mTransport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07004069 private IRestoreObserver mObserver;
Dan Egnor156411d2009-06-26 13:20:02 -07004070 private long mToken;
Christopher Tate84725812010-02-04 15:52:40 -08004071 private PackageInfo mTargetPackage;
Christopher Tate5cb400b2009-06-25 16:03:14 -07004072 private File mStateDir;
Christopher Tate1bb69062010-02-19 17:02:12 -08004073 private int mPmToken;
Chris Tate249345b2010-10-29 12:57:04 -07004074 private boolean mNeedFullBackup;
Christopher Tate284f1bb2011-07-07 14:31:18 -07004075 private HashSet<String> mFilterSet;
Christopher Tate2982d062011-09-06 20:35:24 -07004076 private long mStartRealtime;
4077 private PackageManagerBackupAgent mPmAgent;
4078 private List<PackageInfo> mAgentPackages;
4079 private ArrayList<PackageInfo> mRestorePackages;
4080 private RestoreState mCurrentState;
4081 private int mCount;
4082 private boolean mFinished;
4083 private int mStatus;
4084 private File mBackupDataName;
4085 private File mNewStateName;
4086 private File mSavedStateName;
4087 private ParcelFileDescriptor mBackupData;
4088 private ParcelFileDescriptor mNewState;
4089 private PackageInfo mCurrentPackage;
4090
Christopher Tatedf01dea2009-06-09 20:45:02 -07004091
Christopher Tate5cbbf562009-06-22 16:44:51 -07004092 class RestoreRequest {
4093 public PackageInfo app;
4094 public int storedAppVersion;
4095
4096 RestoreRequest(PackageInfo _app, int _version) {
4097 app = _app;
4098 storedAppVersion = _version;
4099 }
4100 }
4101
Christopher Tate44a27902010-01-27 17:15:49 -08004102 PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
Chris Tate249345b2010-10-29 12:57:04 -07004103 long restoreSetToken, PackageInfo targetPackage, int pmToken,
Christopher Tate284f1bb2011-07-07 14:31:18 -07004104 boolean needFullBackup, String[] filterSet) {
Christopher Tate2982d062011-09-06 20:35:24 -07004105 mCurrentState = RestoreState.INITIAL;
4106 mFinished = false;
4107 mPmAgent = null;
4108
Christopher Tatedf01dea2009-06-09 20:45:02 -07004109 mTransport = transport;
Christopher Tate7d562ec2009-06-25 18:03:43 -07004110 mObserver = observer;
Christopher Tate9bbc21a2009-06-10 20:23:25 -07004111 mToken = restoreSetToken;
Christopher Tate84725812010-02-04 15:52:40 -08004112 mTargetPackage = targetPackage;
Christopher Tate1bb69062010-02-19 17:02:12 -08004113 mPmToken = pmToken;
Chris Tate249345b2010-10-29 12:57:04 -07004114 mNeedFullBackup = needFullBackup;
Christopher Tate5cb400b2009-06-25 16:03:14 -07004115
Christopher Tate284f1bb2011-07-07 14:31:18 -07004116 if (filterSet != null) {
4117 mFilterSet = new HashSet<String>();
4118 for (String pkg : filterSet) {
4119 mFilterSet.add(pkg);
4120 }
4121 } else {
4122 mFilterSet = null;
4123 }
4124
Christopher Tate5cb400b2009-06-25 16:03:14 -07004125 try {
4126 mStateDir = new File(mBaseStateDir, transport.transportDirName());
4127 } catch (RemoteException e) {
4128 // can't happen; the transport is local
4129 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07004130 }
4131
Christopher Tate2982d062011-09-06 20:35:24 -07004132 // Execute one tick of whatever state machine the task implements
4133 @Override
4134 public void execute() {
4135 if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step: " + mCurrentState);
4136 switch (mCurrentState) {
4137 case INITIAL:
4138 beginRestore();
4139 break;
Christopher Tatedf01dea2009-06-09 20:45:02 -07004140
Christopher Tate2982d062011-09-06 20:35:24 -07004141 case DOWNLOAD_DATA:
4142 downloadRestoreData();
4143 break;
Christopher Tate7d562ec2009-06-25 18:03:43 -07004144
Christopher Tate2982d062011-09-06 20:35:24 -07004145 case PM_METADATA:
4146 restorePmMetadata();
4147 break;
4148
4149 case RUNNING_QUEUE:
4150 restoreNextAgent();
4151 break;
4152
4153 case FINAL:
4154 if (!mFinished) finalizeRestore();
4155 else {
4156 Slog.e(TAG, "Duplicate finish");
4157 }
4158 mFinished = true;
4159 break;
4160 }
4161 }
4162
4163 // Initialize and set up for the PM metadata restore, which comes first
4164 void beginRestore() {
4165 // Don't account time doing the restore as inactivity of the app
4166 // that has opened a restore session.
4167 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
4168
4169 // Assume error until we successfully init everything
4170 mStatus = BackupConstants.TRANSPORT_ERROR;
4171
Christopher Tatedf01dea2009-06-09 20:45:02 -07004172 try {
Dan Egnorbb9001c2009-07-27 12:20:13 -07004173 // TODO: Log this before getAvailableRestoreSets, somehow
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004174 EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
Christopher Tateabce4e82009-06-18 18:35:32 -07004175
Dan Egnorefe52642009-06-24 00:16:33 -07004176 // Get the list of all packages which have backup enabled.
4177 // (Include the Package Manager metadata pseudo-package first.)
Christopher Tate2982d062011-09-06 20:35:24 -07004178 mRestorePackages = new ArrayList<PackageInfo>();
Dan Egnorefe52642009-06-24 00:16:33 -07004179 PackageInfo omPackage = new PackageInfo();
4180 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
Christopher Tate2982d062011-09-06 20:35:24 -07004181 mRestorePackages.add(omPackage);
Christopher Tatedf01dea2009-06-09 20:45:02 -07004182
Christopher Tate2982d062011-09-06 20:35:24 -07004183 mAgentPackages = allAgentPackages();
Christopher Tate84725812010-02-04 15:52:40 -08004184 if (mTargetPackage == null) {
Christopher Tate284f1bb2011-07-07 14:31:18 -07004185 // if there's a filter set, strip out anything that isn't
4186 // present before proceeding
4187 if (mFilterSet != null) {
Christopher Tate2982d062011-09-06 20:35:24 -07004188 for (int i = mAgentPackages.size() - 1; i >= 0; i--) {
4189 final PackageInfo pkg = mAgentPackages.get(i);
Christopher Tate284f1bb2011-07-07 14:31:18 -07004190 if (! mFilterSet.contains(pkg.packageName)) {
Christopher Tate2982d062011-09-06 20:35:24 -07004191 mAgentPackages.remove(i);
Christopher Tate284f1bb2011-07-07 14:31:18 -07004192 }
4193 }
Christopher Tate2982d062011-09-06 20:35:24 -07004194 if (MORE_DEBUG) {
Christopher Tate284f1bb2011-07-07 14:31:18 -07004195 Slog.i(TAG, "Post-filter package set for restore:");
Christopher Tate2982d062011-09-06 20:35:24 -07004196 for (PackageInfo p : mAgentPackages) {
Christopher Tate284f1bb2011-07-07 14:31:18 -07004197 Slog.i(TAG, " " + p);
4198 }
4199 }
4200 }
Christopher Tate2982d062011-09-06 20:35:24 -07004201 mRestorePackages.addAll(mAgentPackages);
Christopher Tate84725812010-02-04 15:52:40 -08004202 } else {
4203 // Just one package to attempt restore of
Christopher Tate2982d062011-09-06 20:35:24 -07004204 mRestorePackages.add(mTargetPackage);
Christopher Tate84725812010-02-04 15:52:40 -08004205 }
Dan Egnorefe52642009-06-24 00:16:33 -07004206
Christopher Tate7d562ec2009-06-25 18:03:43 -07004207 // let the observer know that we're running
4208 if (mObserver != null) {
4209 try {
4210 // !!! TODO: get an actual count from the transport after
4211 // its startRestore() runs?
Christopher Tate2982d062011-09-06 20:35:24 -07004212 mObserver.restoreStarting(mRestorePackages.size());
Christopher Tate7d562ec2009-06-25 18:03:43 -07004213 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004214 Slog.d(TAG, "Restore observer died at restoreStarting");
Christopher Tate7d562ec2009-06-25 18:03:43 -07004215 mObserver = null;
4216 }
4217 }
Christopher Tate2982d062011-09-06 20:35:24 -07004218 } catch (RemoteException e) {
4219 // Something has gone catastrophically wrong with the transport
4220 Slog.e(TAG, "Error communicating with transport for restore");
4221 executeNextState(RestoreState.FINAL);
4222 return;
4223 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07004224
Christopher Tate2982d062011-09-06 20:35:24 -07004225 mStatus = BackupConstants.TRANSPORT_OK;
4226 executeNextState(RestoreState.DOWNLOAD_DATA);
4227 }
4228
4229 void downloadRestoreData() {
4230 // Note that the download phase can be very time consuming, but we're executing
4231 // it inline here on the looper. This is "okay" because it is not calling out to
4232 // third party code; the transport is "trusted," and so we assume it is being a
4233 // good citizen and timing out etc when appropriate.
4234 //
4235 // TODO: when appropriate, move the download off the looper and rearrange the
4236 // error handling around that.
4237 try {
4238 mStatus = mTransport.startRestore(mToken,
4239 mRestorePackages.toArray(new PackageInfo[0]));
4240 if (mStatus != BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004241 Slog.e(TAG, "Error starting restore operation");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004242 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Christopher Tate2982d062011-09-06 20:35:24 -07004243 executeNextState(RestoreState.FINAL);
Dan Egnorefe52642009-06-24 00:16:33 -07004244 return;
4245 }
Christopher Tate2982d062011-09-06 20:35:24 -07004246 } catch (RemoteException e) {
4247 Slog.e(TAG, "Error communicating with transport for restore");
4248 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
4249 mStatus = BackupConstants.TRANSPORT_ERROR;
4250 executeNextState(RestoreState.FINAL);
4251 return;
4252 }
Dan Egnorefe52642009-06-24 00:16:33 -07004253
Christopher Tate2982d062011-09-06 20:35:24 -07004254 // Successful download of the data to be parceled out to the apps, so off we go.
4255 executeNextState(RestoreState.PM_METADATA);
4256 }
4257
4258 void restorePmMetadata() {
4259 try {
Dan Egnorefe52642009-06-24 00:16:33 -07004260 String packageName = mTransport.nextRestorePackage();
4261 if (packageName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004262 Slog.e(TAG, "Error getting first restore package");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004263 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Christopher Tate2982d062011-09-06 20:35:24 -07004264 mStatus = BackupConstants.TRANSPORT_ERROR;
4265 executeNextState(RestoreState.FINAL);
Dan Egnorefe52642009-06-24 00:16:33 -07004266 return;
4267 } else if (packageName.equals("")) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004268 Slog.i(TAG, "No restore data available");
Christopher Tate2982d062011-09-06 20:35:24 -07004269 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004270 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
Christopher Tate2982d062011-09-06 20:35:24 -07004271 mStatus = BackupConstants.TRANSPORT_OK;
4272 executeNextState(RestoreState.FINAL);
Dan Egnorefe52642009-06-24 00:16:33 -07004273 return;
4274 } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004275 Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
Christopher Tate2982d062011-09-06 20:35:24 -07004276 + "\", found only \"" + packageName + "\"");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004277 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Dan Egnorbb9001c2009-07-27 12:20:13 -07004278 "Package manager data missing");
Christopher Tate2982d062011-09-06 20:35:24 -07004279 executeNextState(RestoreState.FINAL);
Dan Egnorefe52642009-06-24 00:16:33 -07004280 return;
4281 }
4282
4283 // Pull the Package Manager metadata from the restore set first
Christopher Tate2982d062011-09-06 20:35:24 -07004284 PackageInfo omPackage = new PackageInfo();
4285 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
4286 mPmAgent = new PackageManagerBackupAgent(
4287 mPackageManager, mAgentPackages);
4288 initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()),
Chris Tate249345b2010-10-29 12:57:04 -07004289 mNeedFullBackup);
Christopher Tate2982d062011-09-06 20:35:24 -07004290 // The PM agent called operationComplete() already, because our invocation
4291 // of it is process-local and therefore synchronous. That means that a
4292 // RUNNING_QUEUE message is already enqueued. Only if we're unable to
4293 // proceed with running the queue do we remove that pending message and
4294 // jump straight to the FINAL state.
Dan Egnorefe52642009-06-24 00:16:33 -07004295
Christopher Tate8c032472009-07-02 14:28:47 -07004296 // Verify that the backup set includes metadata. If not, we can't do
4297 // signature/version verification etc, so we simply do not proceed with
4298 // the restore operation.
Christopher Tate2982d062011-09-06 20:35:24 -07004299 if (!mPmAgent.hasMetadata()) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004300 Slog.e(TAG, "No restore metadata available, so not restoring settings");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004301 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
Christopher Tate2982d062011-09-06 20:35:24 -07004302 "Package manager restore metadata missing");
4303 mStatus = BackupConstants.TRANSPORT_ERROR;
4304 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
4305 executeNextState(RestoreState.FINAL);
Christopher Tate8c032472009-07-02 14:28:47 -07004306 return;
4307 }
Christopher Tate2982d062011-09-06 20:35:24 -07004308 } catch (RemoteException e) {
4309 Slog.e(TAG, "Error communicating with transport for restore");
4310 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
4311 mStatus = BackupConstants.TRANSPORT_ERROR;
4312 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
4313 executeNextState(RestoreState.FINAL);
4314 return;
4315 }
Christopher Tate8c032472009-07-02 14:28:47 -07004316
Christopher Tate2982d062011-09-06 20:35:24 -07004317 // Metadata is intact, so we can now run the restore queue. If we get here,
4318 // we have already enqueued the necessary next-step message on the looper.
4319 }
Dan Egnorbb9001c2009-07-27 12:20:13 -07004320
Christopher Tate2982d062011-09-06 20:35:24 -07004321 void restoreNextAgent() {
4322 try {
4323 String packageName = mTransport.nextRestorePackage();
Christopher Tatedf01dea2009-06-09 20:45:02 -07004324
Christopher Tate2982d062011-09-06 20:35:24 -07004325 if (packageName == null) {
4326 Slog.e(TAG, "Error getting next restore package");
4327 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
4328 executeNextState(RestoreState.FINAL);
4329 return;
4330 } else if (packageName.equals("")) {
4331 if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
4332 int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
4333 EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
4334 executeNextState(RestoreState.FINAL);
4335 return;
Dan Egnorefe52642009-06-24 00:16:33 -07004336 }
Christopher Tate7d562ec2009-06-25 18:03:43 -07004337
4338 if (mObserver != null) {
4339 try {
Christopher Tate2982d062011-09-06 20:35:24 -07004340 mObserver.onUpdate(mCount, packageName);
Christopher Tate7d562ec2009-06-25 18:03:43 -07004341 } catch (RemoteException e) {
Christopher Tate2982d062011-09-06 20:35:24 -07004342 Slog.d(TAG, "Restore observer died in onUpdate");
4343 mObserver = null;
Christopher Tate7d562ec2009-06-25 18:03:43 -07004344 }
4345 }
Christopher Tateb6787f22009-07-02 17:40:45 -07004346
Christopher Tate2982d062011-09-06 20:35:24 -07004347 Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
4348 if (metaInfo == null) {
4349 Slog.e(TAG, "Missing metadata for " + packageName);
4350 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
4351 "Package metadata missing");
4352 executeNextState(RestoreState.RUNNING_QUEUE);
4353 return;
Christopher Tate84725812010-02-04 15:52:40 -08004354 }
4355
Christopher Tate2982d062011-09-06 20:35:24 -07004356 PackageInfo packageInfo;
4357 try {
4358 int flags = PackageManager.GET_SIGNATURES;
4359 packageInfo = mPackageManager.getPackageInfo(packageName, flags);
4360 } catch (NameNotFoundException e) {
4361 Slog.e(TAG, "Invalid package restoring data", e);
4362 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
4363 "Package missing on device");
4364 executeNextState(RestoreState.RUNNING_QUEUE);
4365 return;
Christopher Tate1bb69062010-02-19 17:02:12 -08004366 }
4367
Christopher Tate2982d062011-09-06 20:35:24 -07004368 if (metaInfo.versionCode > packageInfo.versionCode) {
4369 // Data is from a "newer" version of the app than we have currently
4370 // installed. If the app has not declared that it is prepared to
4371 // handle this case, we do not attempt the restore.
4372 if ((packageInfo.applicationInfo.flags
4373 & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
4374 String message = "Version " + metaInfo.versionCode
4375 + " > installed version " + packageInfo.versionCode;
4376 Slog.w(TAG, "Package " + packageName + ": " + message);
4377 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
4378 packageName, message);
4379 executeNextState(RestoreState.RUNNING_QUEUE);
4380 return;
4381 } else {
4382 if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
4383 + " > installed " + packageInfo.versionCode
4384 + " but restoreAnyVersion");
4385 }
4386 }
Christopher Tate73a3cb32010-12-13 18:27:26 -08004387
Christopher Tate2982d062011-09-06 20:35:24 -07004388 if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
4389 Slog.w(TAG, "Signature mismatch restoring " + packageName);
4390 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
4391 "Signature mismatch");
4392 executeNextState(RestoreState.RUNNING_QUEUE);
4393 return;
4394 }
4395
4396 if (DEBUG) Slog.v(TAG, "Package " + packageName
4397 + " restore version [" + metaInfo.versionCode
4398 + "] is compatible with installed version ["
4399 + packageInfo.versionCode + "]");
4400
4401 // Then set up and bind the agent
4402 IBackupAgent agent = bindToAgentSynchronous(
4403 packageInfo.applicationInfo,
4404 IApplicationThread.BACKUP_MODE_INCREMENTAL);
4405 if (agent == null) {
4406 Slog.w(TAG, "Can't find backup agent for " + packageName);
4407 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
4408 "Restore agent missing");
4409 executeNextState(RestoreState.RUNNING_QUEUE);
4410 return;
4411 }
4412
4413 // And then finally start the restore on this agent
4414 try {
4415 initiateOneRestore(packageInfo, metaInfo.versionCode, agent, mNeedFullBackup);
4416 ++mCount;
4417 } catch (Exception e) {
4418 Slog.e(TAG, "Error when attempting restore: " + e.toString());
4419 agentErrorCleanup();
4420 executeNextState(RestoreState.RUNNING_QUEUE);
4421 }
4422 } catch (RemoteException e) {
4423 Slog.e(TAG, "Unable to fetch restore data from transport");
4424 mStatus = BackupConstants.TRANSPORT_ERROR;
4425 executeNextState(RestoreState.FINAL);
Christopher Tatedf01dea2009-06-09 20:45:02 -07004426 }
4427 }
4428
Christopher Tate2982d062011-09-06 20:35:24 -07004429 void finalizeRestore() {
4430 if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
4431
4432 try {
4433 mTransport.finishRestore();
4434 } catch (RemoteException e) {
4435 Slog.e(TAG, "Error finishing restore", e);
4436 }
4437
4438 if (mObserver != null) {
4439 try {
4440 mObserver.restoreFinished(mStatus);
4441 } catch (RemoteException e) {
4442 Slog.d(TAG, "Restore observer died at restoreFinished");
4443 }
4444 }
4445
4446 // If this was a restoreAll operation, record that this was our
4447 // ancestral dataset, as well as the set of apps that are possibly
4448 // restoreable from the dataset
4449 if (mTargetPackage == null && mPmAgent != null) {
4450 mAncestralPackages = mPmAgent.getRestoredPackages();
4451 mAncestralToken = mToken;
4452 writeRestoreTokens();
4453 }
4454
4455 // We must under all circumstances tell the Package Manager to
4456 // proceed with install notifications if it's waiting for us.
4457 if (mPmToken > 0) {
4458 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
4459 try {
4460 mPackageManagerBinder.finishPackageInstall(mPmToken);
4461 } catch (RemoteException e) { /* can't happen */ }
4462 }
4463
4464 // Furthermore we need to reset the session timeout clock
4465 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
4466 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
4467 TIMEOUT_RESTORE_INTERVAL);
4468
4469 // done; we can finally release the wakelock
4470 Slog.i(TAG, "Restore complete.");
4471 mWakelock.release();
4472 }
4473
4474 // Call asynchronously into the app, passing it the restore data. The next step
4475 // after this is always a callback, either operationComplete() or handleTimeout().
4476 void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
Chris Tate249345b2010-10-29 12:57:04 -07004477 boolean needFullBackup) {
Christopher Tate2982d062011-09-06 20:35:24 -07004478 mCurrentPackage = app;
Christopher Tatec7b31e32009-06-10 15:49:30 -07004479 final String packageName = app.packageName;
4480
Christopher Tate2982d062011-09-06 20:35:24 -07004481 if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04004482
Christopher Tatec7b31e32009-06-10 15:49:30 -07004483 // !!! TODO: get the dirs from the transport
Christopher Tate2982d062011-09-06 20:35:24 -07004484 mBackupDataName = new File(mDataDir, packageName + ".restore");
4485 mNewStateName = new File(mStateDir, packageName + ".new");
4486 mSavedStateName = new File(mStateDir, packageName);
Dan Egnorbb9001c2009-07-27 12:20:13 -07004487
Christopher Tate4a627c72011-04-01 14:43:32 -07004488 final int token = generateToken();
Dan Egnorbb9001c2009-07-27 12:20:13 -07004489 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -07004490 // Run the transport's restore pass
Christopher Tate2982d062011-09-06 20:35:24 -07004491 mBackupData = ParcelFileDescriptor.open(mBackupDataName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07004492 ParcelFileDescriptor.MODE_READ_WRITE |
4493 ParcelFileDescriptor.MODE_CREATE |
4494 ParcelFileDescriptor.MODE_TRUNCATE);
4495
Christopher Tate2982d062011-09-06 20:35:24 -07004496 if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
Christopher Tate5f2f4132011-09-26 13:10:38 -07004497 // Transport-level failure, so we wind everything up and
4498 // terminate the restore operation.
Joe Onorato8a9b2202010-02-26 18:56:32 -08004499 Slog.e(TAG, "Error getting restore data for " + packageName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004500 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
Christopher Tate5f2f4132011-09-26 13:10:38 -07004501 mBackupData.close();
4502 mBackupDataName.delete();
4503 executeNextState(RestoreState.FINAL);
Dan Egnorbb9001c2009-07-27 12:20:13 -07004504 return;
Christopher Tatec7b31e32009-06-10 15:49:30 -07004505 }
4506
4507 // Okay, we have the data. Now have the agent do the restore.
Christopher Tate2982d062011-09-06 20:35:24 -07004508 mBackupData.close();
4509 mBackupData = ParcelFileDescriptor.open(mBackupDataName,
Christopher Tatec7b31e32009-06-10 15:49:30 -07004510 ParcelFileDescriptor.MODE_READ_ONLY);
4511
Christopher Tate2982d062011-09-06 20:35:24 -07004512 mNewState = ParcelFileDescriptor.open(mNewStateName,
Dan Egnorbb9001c2009-07-27 12:20:13 -07004513 ParcelFileDescriptor.MODE_READ_WRITE |
4514 ParcelFileDescriptor.MODE_CREATE |
4515 ParcelFileDescriptor.MODE_TRUNCATE);
4516
Christopher Tate44a27902010-01-27 17:15:49 -08004517 // Kick off the restore, checking for hung agents
Christopher Tate2982d062011-09-06 20:35:24 -07004518 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
4519 agent.doRestore(mBackupData, appVersionCode, mNewState, token, mBackupManagerBinder);
Christopher Tatec7b31e32009-06-10 15:49:30 -07004520 } catch (Exception e) {
Christopher Tate2982d062011-09-06 20:35:24 -07004521 Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004522 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
Christopher Tate2982d062011-09-06 20:35:24 -07004523 agentErrorCleanup(); // clears any pending timeout messages as well
Dan Egnorbb9001c2009-07-27 12:20:13 -07004524
Christopher Tate2982d062011-09-06 20:35:24 -07004525 // After a restore failure we go back to running the queue. If there
4526 // are no more packages to be restored that will be handled by the
4527 // next step.
4528 executeNextState(RestoreState.RUNNING_QUEUE);
4529 }
4530 }
Chris Tate249345b2010-10-29 12:57:04 -07004531
Christopher Tate2982d062011-09-06 20:35:24 -07004532 void agentErrorCleanup() {
4533 // If the agent fails restore, it might have put the app's data
4534 // into an incoherent state. For consistency we wipe its data
4535 // again in this case before continuing with normal teardown
4536 clearApplicationDataSynchronous(mCurrentPackage.packageName);
4537 agentCleanup();
4538 }
4539
4540 void agentCleanup() {
4541 mBackupDataName.delete();
4542 try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
4543 try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
4544 mBackupData = mNewState = null;
4545
4546 // if everything went okay, remember the recorded state now
4547 //
4548 // !!! TODO: the restored data should be migrated on the server
4549 // side into the current dataset. In that case the new state file
4550 // we just created would reflect the data already extant in the
4551 // backend, so there'd be nothing more to do. Until that happens,
4552 // however, we need to make sure that we record the data to the
4553 // current backend dataset. (Yes, this means shipping the data over
4554 // the wire in both directions. That's bad, but consistency comes
4555 // first, then efficiency.) Once we introduce server-side data
4556 // migration to the newly-restored device's dataset, we will change
4557 // the following from a discard of the newly-written state to the
4558 // "correct" operation of renaming into the canonical state blob.
4559 mNewStateName.delete(); // TODO: remove; see above comment
4560 //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this
4561
4562 // If this wasn't the PM pseudopackage, tear down the agent side
4563 if (mCurrentPackage.applicationInfo != null) {
4564 // unbind and tidy up even on timeout or failure
4565 try {
4566 mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
4567
4568 // The agent was probably running with a stub Application object,
4569 // which isn't a valid run mode for the main app logic. Shut
4570 // down the app so that next time it's launched, it gets the
4571 // usual full initialization. Note that this is only done for
4572 // full-system restores: when a single app has requested a restore,
4573 // it is explicitly not killed following that operation.
4574 if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags
4575 & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
4576 if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
4577 + mCurrentPackage.applicationInfo.processName);
4578 mActivityManager.killApplicationProcess(
4579 mCurrentPackage.applicationInfo.processName,
4580 mCurrentPackage.applicationInfo.uid);
4581 }
4582 } catch (RemoteException e) {
4583 // can't happen; we run in the same process as the activity manager
Chris Tate249345b2010-10-29 12:57:04 -07004584 }
Christopher Tatec7b31e32009-06-10 15:49:30 -07004585 }
Christopher Tate2982d062011-09-06 20:35:24 -07004586
4587 // The caller is responsible for reestablishing the state machine; our
4588 // responsibility here is to clear the decks for whatever comes next.
4589 mBackupHandler.removeMessages(MSG_TIMEOUT, this);
4590 synchronized (mCurrentOpLock) {
4591 mCurrentOperations.clear();
4592 }
4593 }
4594
4595 // A call to agent.doRestore() has been positively acknowledged as complete
4596 @Override
4597 public void operationComplete() {
4598 int size = (int) mBackupDataName.length();
4599 EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
4600 // Just go back to running the restore queue
4601 agentCleanup();
4602
4603 executeNextState(RestoreState.RUNNING_QUEUE);
4604 }
4605
4606 // A call to agent.doRestore() has timed out
4607 @Override
4608 public void handleTimeout() {
4609 Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
4610 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
4611 mCurrentPackage.packageName, "restore timeout");
4612 // Handle like an agent that threw on invocation: wipe it and go on to the next
4613 agentErrorCleanup();
4614 executeNextState(RestoreState.RUNNING_QUEUE);
4615 }
4616
4617 void executeNextState(RestoreState nextState) {
4618 if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
4619 + this + " nextState=" + nextState);
4620 mCurrentState = nextState;
4621 Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
4622 mBackupHandler.sendMessage(msg);
Christopher Tatedf01dea2009-06-09 20:45:02 -07004623 }
4624 }
4625
Christopher Tate44a27902010-01-27 17:15:49 -08004626 class PerformClearTask implements Runnable {
Christopher Tateee0e78a2009-07-02 11:17:03 -07004627 IBackupTransport mTransport;
4628 PackageInfo mPackage;
4629
Christopher Tate44a27902010-01-27 17:15:49 -08004630 PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
Christopher Tateee0e78a2009-07-02 11:17:03 -07004631 mTransport = transport;
4632 mPackage = packageInfo;
4633 }
4634
Christopher Tateee0e78a2009-07-02 11:17:03 -07004635 public void run() {
4636 try {
4637 // Clear the on-device backup state to ensure a full backup next time
4638 File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
4639 File stateFile = new File(stateDir, mPackage.packageName);
4640 stateFile.delete();
4641
4642 // Tell the transport to remove all the persistent storage for the app
Christopher Tate13f4a642009-09-30 20:06:45 -07004643 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07004644 mTransport.clearBackupData(mPackage);
4645 } catch (RemoteException e) {
4646 // can't happen; the transport is local
Christopher Tate0abf6a02012-03-23 17:45:15 -07004647 } catch (Exception e) {
4648 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
Christopher Tateee0e78a2009-07-02 11:17:03 -07004649 } finally {
4650 try {
Christopher Tate13f4a642009-09-30 20:06:45 -07004651 // TODO - need to handle failures
Christopher Tateee0e78a2009-07-02 11:17:03 -07004652 mTransport.finishBackup();
4653 } catch (RemoteException e) {
4654 // can't happen; the transport is local
4655 }
Christopher Tateb6787f22009-07-02 17:40:45 -07004656
4657 // Last but not least, release the cpu
4658 mWakelock.release();
Christopher Tateee0e78a2009-07-02 11:17:03 -07004659 }
4660 }
4661 }
4662
Christopher Tate44a27902010-01-27 17:15:49 -08004663 class PerformInitializeTask implements Runnable {
Christopher Tate4cc86e12009-09-21 19:36:51 -07004664 HashSet<String> mQueue;
4665
Christopher Tate44a27902010-01-27 17:15:49 -08004666 PerformInitializeTask(HashSet<String> transportNames) {
Christopher Tate4cc86e12009-09-21 19:36:51 -07004667 mQueue = transportNames;
4668 }
4669
Christopher Tate4cc86e12009-09-21 19:36:51 -07004670 public void run() {
Christopher Tate4cc86e12009-09-21 19:36:51 -07004671 try {
4672 for (String transportName : mQueue) {
4673 IBackupTransport transport = getTransport(transportName);
4674 if (transport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004675 Slog.e(TAG, "Requested init for " + transportName + " but not found");
Christopher Tate4cc86e12009-09-21 19:36:51 -07004676 continue;
4677 }
4678
Joe Onorato8a9b2202010-02-26 18:56:32 -08004679 Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004680 EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
Dan Egnor726247c2009-09-29 19:12:31 -07004681 long startRealtime = SystemClock.elapsedRealtime();
4682 int status = transport.initializeDevice();
Christopher Tate4cc86e12009-09-21 19:36:51 -07004683
Christopher Tate4cc86e12009-09-21 19:36:51 -07004684 if (status == BackupConstants.TRANSPORT_OK) {
4685 status = transport.finishBackup();
4686 }
4687
4688 // Okay, the wipe really happened. Clean up our local bookkeeping.
4689 if (status == BackupConstants.TRANSPORT_OK) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004690 Slog.i(TAG, "Device init successful");
Dan Egnor726247c2009-09-29 19:12:31 -07004691 int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004692 EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
Dan Egnor726247c2009-09-29 19:12:31 -07004693 resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004694 EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
Christopher Tate4cc86e12009-09-21 19:36:51 -07004695 synchronized (mQueueLock) {
4696 recordInitPendingLocked(false, transportName);
4697 }
Dan Egnor726247c2009-09-29 19:12:31 -07004698 } else {
4699 // If this didn't work, requeue this one and try again
4700 // after a suitable interval
Joe Onorato8a9b2202010-02-26 18:56:32 -08004701 Slog.e(TAG, "Transport error in initializeDevice()");
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004702 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
Christopher Tate4cc86e12009-09-21 19:36:51 -07004703 synchronized (mQueueLock) {
4704 recordInitPendingLocked(true, transportName);
4705 }
4706 // do this via another alarm to make sure of the wakelock states
4707 long delay = transport.requestBackupTime();
Joe Onorato8a9b2202010-02-26 18:56:32 -08004708 if (DEBUG) Slog.w(TAG, "init failed on "
Christopher Tate4cc86e12009-09-21 19:36:51 -07004709 + transportName + " resched in " + delay);
4710 mAlarmManager.set(AlarmManager.RTC_WAKEUP,
4711 System.currentTimeMillis() + delay, mRunInitIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07004712 }
Christopher Tate4cc86e12009-09-21 19:36:51 -07004713 }
4714 } catch (RemoteException e) {
4715 // can't happen; the transports are local
4716 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004717 Slog.e(TAG, "Unexpected error performing init", e);
Christopher Tate4cc86e12009-09-21 19:36:51 -07004718 } finally {
Christopher Tatec2af5d32010-02-02 15:18:58 -08004719 // Done; release the wakelock
Christopher Tate4cc86e12009-09-21 19:36:51 -07004720 mWakelock.release();
4721 }
4722 }
4723 }
4724
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004725 private void dataChangedImpl(String packageName) {
Christopher Tatea3d55342012-03-27 13:16:18 -07004726 HashSet<String> targets = dataChangedTargets(packageName);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004727 dataChangedImpl(packageName, targets);
4728 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07004729
Christopher Tatea3d55342012-03-27 13:16:18 -07004730 private void dataChangedImpl(String packageName, HashSet<String> targets) {
Christopher Tate487529a2009-04-29 14:03:25 -07004731 // Record that we need a backup pass for the caller. Since multiple callers
4732 // may share a uid, we need to note all candidates within that uid and schedule
4733 // a backup pass for each of them.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004734 EventLog.writeEvent(EventLogTags.BACKUP_DATA_CHANGED, packageName);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004735
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004736 if (targets == null) {
4737 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
4738 + " uid=" + Binder.getCallingUid());
4739 return;
4740 }
4741
4742 synchronized (mQueueLock) {
4743 // Note that this client has made data changes that need to be backed up
Christopher Tatea3d55342012-03-27 13:16:18 -07004744 if (targets.contains(packageName)) {
4745 // Add the caller to the set of pending backups. If there is
4746 // one already there, then overwrite it, but no harm done.
4747 BackupRequest req = new BackupRequest(packageName);
4748 if (mPendingBackups.put(packageName, req) == null) {
4749 if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
Christopher Tate6de74ff2012-01-17 15:20:32 -08004750
Christopher Tatea3d55342012-03-27 13:16:18 -07004751 // Journal this request in case of crash. The put()
4752 // operation returned null when this package was not already
4753 // in the set; we want to avoid touching the disk redundantly.
4754 writeToJournalLocked(packageName);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004755
Christopher Tatea3d55342012-03-27 13:16:18 -07004756 if (MORE_DEBUG) {
4757 int numKeys = mPendingBackups.size();
4758 Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
4759 for (BackupRequest b : mPendingBackups.values()) {
4760 Slog.d(TAG, " + " + b);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004761 }
4762 }
4763 }
4764 }
4765 }
4766 }
4767
4768 // Note: packageName is currently unused, but may be in the future
Christopher Tatea3d55342012-03-27 13:16:18 -07004769 private HashSet<String> dataChangedTargets(String packageName) {
Christopher Tate63d27002009-06-16 17:16:42 -07004770 // If the caller does not hold the BACKUP permission, it can only request a
4771 // backup of its own data.
Dianne Hackborncf098292009-07-01 19:55:20 -07004772 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tate63d27002009-06-16 17:16:42 -07004773 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004774 synchronized (mBackupParticipants) {
4775 return mBackupParticipants.get(Binder.getCallingUid());
4776 }
4777 }
4778
4779 // a caller with full permission can ask to back up any participating app
4780 // !!! TODO: allow backup of ANY app?
Christopher Tatea3d55342012-03-27 13:16:18 -07004781 HashSet<String> targets = new HashSet<String>();
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004782 synchronized (mBackupParticipants) {
Christopher Tate63d27002009-06-16 17:16:42 -07004783 int N = mBackupParticipants.size();
4784 for (int i = 0; i < N; i++) {
Christopher Tatea3d55342012-03-27 13:16:18 -07004785 HashSet<String> s = mBackupParticipants.valueAt(i);
Christopher Tate63d27002009-06-16 17:16:42 -07004786 if (s != null) {
4787 targets.addAll(s);
4788 }
4789 }
4790 }
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004791 return targets;
Christopher Tate487529a2009-04-29 14:03:25 -07004792 }
Christopher Tate46758122009-05-06 11:22:00 -07004793
Christopher Tatecde87f42009-06-12 12:55:53 -07004794 private void writeToJournalLocked(String str) {
Dan Egnor852f8e42009-09-30 11:20:45 -07004795 RandomAccessFile out = null;
4796 try {
4797 if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
4798 out = new RandomAccessFile(mJournal, "rws");
4799 out.seek(out.length());
4800 out.writeUTF(str);
4801 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004802 Slog.e(TAG, "Can't write " + str + " to backup journal", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07004803 mJournal = null;
4804 } finally {
4805 try { if (out != null) out.close(); } catch (IOException e) {}
Christopher Tatecde87f42009-06-12 12:55:53 -07004806 }
4807 }
4808
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004809 // ----- IBackupManager binder interface -----
4810
4811 public void dataChanged(final String packageName) {
Christopher Tatea3d55342012-03-27 13:16:18 -07004812 final HashSet<String> targets = dataChangedTargets(packageName);
Brad Fitzpatrick3dd42332010-09-07 23:40:30 -07004813 if (targets == null) {
4814 Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
4815 + " uid=" + Binder.getCallingUid());
4816 return;
4817 }
4818
4819 mBackupHandler.post(new Runnable() {
4820 public void run() {
4821 dataChangedImpl(packageName, targets);
4822 }
4823 });
4824 }
4825
Christopher Tateee0e78a2009-07-02 11:17:03 -07004826 // Clear the given package's backup data from the current transport
4827 public void clearBackupData(String packageName) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004828 if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName);
Christopher Tateee0e78a2009-07-02 11:17:03 -07004829 PackageInfo info;
4830 try {
4831 info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
4832 } catch (NameNotFoundException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08004833 Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
Christopher Tateee0e78a2009-07-02 11:17:03 -07004834 return;
4835 }
4836
4837 // If the caller does not hold the BACKUP permission, it can only request a
4838 // wipe of its own backed-up data.
Christopher Tatea3d55342012-03-27 13:16:18 -07004839 HashSet<String> apps;
Christopher Tate4e3e50c2009-07-02 12:14:05 -07004840 if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Christopher Tateee0e78a2009-07-02 11:17:03 -07004841 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
4842 apps = mBackupParticipants.get(Binder.getCallingUid());
4843 } else {
4844 // a caller with full permission can ask to back up any participating app
4845 // !!! TODO: allow data-clear of ANY app?
Joe Onorato8a9b2202010-02-26 18:56:32 -08004846 if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
Christopher Tatea3d55342012-03-27 13:16:18 -07004847 apps = new HashSet<String>();
Christopher Tateee0e78a2009-07-02 11:17:03 -07004848 int N = mBackupParticipants.size();
4849 for (int i = 0; i < N; i++) {
Christopher Tatea3d55342012-03-27 13:16:18 -07004850 HashSet<String> s = mBackupParticipants.valueAt(i);
Christopher Tateee0e78a2009-07-02 11:17:03 -07004851 if (s != null) {
4852 apps.addAll(s);
4853 }
4854 }
4855 }
4856
Christopher Tatea3d55342012-03-27 13:16:18 -07004857 // Is the given app an available participant?
4858 if (apps.contains(packageName)) {
4859 if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
4860 // found it; fire off the clear request
4861 synchronized (mQueueLock) {
4862 long oldId = Binder.clearCallingIdentity();
4863 mWakelock.acquire();
4864 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
4865 new ClearParams(getTransport(mCurrentTransport), info));
4866 mBackupHandler.sendMessage(msg);
4867 Binder.restoreCallingIdentity(oldId);
Christopher Tateee0e78a2009-07-02 11:17:03 -07004868 }
4869 }
4870 }
4871
Christopher Tateace7f092009-06-15 18:07:25 -07004872 // Run a backup pass immediately for any applications that have declared
4873 // that they have pending updates.
Dan Egnor852f8e42009-09-30 11:20:45 -07004874 public void backupNow() {
Joe Onorato5933a492009-07-23 18:24:08 -04004875 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
Christopher Tate043dadc2009-06-02 16:11:00 -07004876
Joe Onorato8a9b2202010-02-26 18:56:32 -08004877 if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
Christopher Tate46758122009-05-06 11:22:00 -07004878 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07004879 // Because the alarms we are using can jitter, and we want an *immediate*
4880 // backup pass to happen, we restart the timer beginning with "next time,"
4881 // then manually fire the backup trigger intent ourselves.
4882 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tateb6787f22009-07-02 17:40:45 -07004883 try {
Christopher Tateb6787f22009-07-02 17:40:45 -07004884 mRunBackupIntent.send();
4885 } catch (PendingIntent.CanceledException e) {
4886 // should never happen
Joe Onorato8a9b2202010-02-26 18:56:32 -08004887 Slog.e(TAG, "run-backup intent cancelled!");
Christopher Tateb6787f22009-07-02 17:40:45 -07004888 }
Christopher Tate46758122009-05-06 11:22:00 -07004889 }
4890 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07004891
Christopher Tated2c0cd42011-09-15 15:51:29 -07004892 boolean deviceIsProvisioned() {
4893 final ContentResolver resolver = mContext.getContentResolver();
4894 return (Settings.Secure.getInt(resolver, Settings.Secure.DEVICE_PROVISIONED, 0) != 0);
4895 }
4896
Christopher Tate4a627c72011-04-01 14:43:32 -07004897 // Run a *full* backup pass for the given package, writing the resulting data stream
4898 // to the supplied file descriptor. This method is synchronous and does not return
4899 // to the caller until the backup has been completed.
4900 public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
Christopher Tate240c7d22011-10-03 18:13:44 -07004901 boolean doAllApps, boolean includeSystem, String[] pkgList) {
Christopher Tate4a627c72011-04-01 14:43:32 -07004902 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
4903
4904 // Validate
4905 if (!doAllApps) {
4906 if (!includeShared) {
4907 // If we're backing up shared data (sdcard or equivalent), then we can run
4908 // without any supplied app names. Otherwise, we'd be doing no work, so
4909 // report the error.
4910 if (pkgList == null || pkgList.length == 0) {
4911 throw new IllegalArgumentException(
4912 "Backup requested but neither shared nor any apps named");
4913 }
4914 }
4915 }
4916
Christopher Tate4a627c72011-04-01 14:43:32 -07004917 long oldId = Binder.clearCallingIdentity();
4918 try {
Christopher Tated2c0cd42011-09-15 15:51:29 -07004919 // Doesn't make sense to do a full backup prior to setup
4920 if (!deviceIsProvisioned()) {
4921 Slog.i(TAG, "Full backup not supported before setup");
4922 return;
4923 }
4924
4925 if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
4926 + " shared=" + includeShared + " all=" + doAllApps
4927 + " pkgs=" + pkgList);
4928 Slog.i(TAG, "Beginning full backup...");
4929
Christopher Tate4a627c72011-04-01 14:43:32 -07004930 FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
Christopher Tate240c7d22011-10-03 18:13:44 -07004931 doAllApps, includeSystem, pkgList);
Christopher Tate4a627c72011-04-01 14:43:32 -07004932 final int token = generateToken();
4933 synchronized (mFullConfirmations) {
4934 mFullConfirmations.put(token, params);
4935 }
4936
Christopher Tate75a99702011-05-18 16:28:19 -07004937 // start up the confirmation UI
4938 if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
4939 if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
4940 Slog.e(TAG, "Unable to launch full backup confirmation");
Christopher Tate4a627c72011-04-01 14:43:32 -07004941 mFullConfirmations.delete(token);
4942 return;
4943 }
Christopher Tate75a99702011-05-18 16:28:19 -07004944
4945 // make sure the screen is lit for the user interaction
Christopher Tate4a627c72011-04-01 14:43:32 -07004946 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
4947
4948 // start the confirmation countdown
Christopher Tate75a99702011-05-18 16:28:19 -07004949 startConfirmationTimeout(token, params);
Christopher Tate4a627c72011-04-01 14:43:32 -07004950
4951 // wait for the backup to be performed
4952 if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
4953 waitForCompletion(params);
Christopher Tate4a627c72011-04-01 14:43:32 -07004954 } finally {
Christopher Tate4a627c72011-04-01 14:43:32 -07004955 try {
4956 fd.close();
4957 } catch (IOException e) {
4958 // just eat it
4959 }
Christopher Tate75a99702011-05-18 16:28:19 -07004960 Binder.restoreCallingIdentity(oldId);
Christopher Tated2c0cd42011-09-15 15:51:29 -07004961 Slog.d(TAG, "Full backup processing complete.");
Christopher Tate4a627c72011-04-01 14:43:32 -07004962 }
Christopher Tate75a99702011-05-18 16:28:19 -07004963 }
4964
4965 public void fullRestore(ParcelFileDescriptor fd) {
Christopher Tate2efd2db2011-07-19 16:32:49 -07004966 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
Christopher Tate75a99702011-05-18 16:28:19 -07004967
4968 long oldId = Binder.clearCallingIdentity();
4969
4970 try {
Christopher Tated2c0cd42011-09-15 15:51:29 -07004971 // Check whether the device has been provisioned -- we don't handle
4972 // full restores prior to completing the setup process.
4973 if (!deviceIsProvisioned()) {
4974 Slog.i(TAG, "Full restore not permitted before setup");
4975 return;
4976 }
4977
4978 Slog.i(TAG, "Beginning full restore...");
4979
Christopher Tate75a99702011-05-18 16:28:19 -07004980 FullRestoreParams params = new FullRestoreParams(fd);
4981 final int token = generateToken();
4982 synchronized (mFullConfirmations) {
4983 mFullConfirmations.put(token, params);
4984 }
4985
4986 // start up the confirmation UI
4987 if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
4988 if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
4989 Slog.e(TAG, "Unable to launch full restore confirmation");
4990 mFullConfirmations.delete(token);
4991 return;
4992 }
4993
4994 // make sure the screen is lit for the user interaction
4995 mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
4996
4997 // start the confirmation countdown
4998 startConfirmationTimeout(token, params);
4999
5000 // wait for the restore to be performed
5001 if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
5002 waitForCompletion(params);
5003 } finally {
5004 try {
5005 fd.close();
5006 } catch (IOException e) {
5007 Slog.w(TAG, "Error trying to close fd after full restore: " + e);
5008 }
5009 Binder.restoreCallingIdentity(oldId);
Christopher Tated2c0cd42011-09-15 15:51:29 -07005010 Slog.i(TAG, "Full restore processing complete.");
Christopher Tate75a99702011-05-18 16:28:19 -07005011 }
5012 }
5013
5014 boolean startConfirmationUi(int token, String action) {
5015 try {
5016 Intent confIntent = new Intent(action);
5017 confIntent.setClassName("com.android.backupconfirm",
5018 "com.android.backupconfirm.BackupRestoreConfirmation");
5019 confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
5020 confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5021 mContext.startActivity(confIntent);
5022 } catch (ActivityNotFoundException e) {
5023 return false;
5024 }
5025 return true;
5026 }
5027
5028 void startConfirmationTimeout(int token, FullParams params) {
Christopher Tatec58efa62011-08-01 19:20:14 -07005029 if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
Christopher Tate75a99702011-05-18 16:28:19 -07005030 + TIMEOUT_FULL_CONFIRMATION + " millis");
5031 Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
5032 token, 0, params);
5033 mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
Christopher Tate4a627c72011-04-01 14:43:32 -07005034 }
5035
5036 void waitForCompletion(FullParams params) {
5037 synchronized (params.latch) {
5038 while (params.latch.get() == false) {
5039 try {
5040 params.latch.wait();
5041 } catch (InterruptedException e) { /* never interrupted */ }
5042 }
5043 }
5044 }
5045
5046 void signalFullBackupRestoreCompletion(FullParams params) {
5047 synchronized (params.latch) {
5048 params.latch.set(true);
5049 params.latch.notifyAll();
5050 }
5051 }
5052
5053 // Confirm that the previously-requested full backup/restore operation can proceed. This
5054 // is used to require a user-facing disclosure about the operation.
Christopher Tate2efd2db2011-07-19 16:32:49 -07005055 @Override
Christopher Tate4a627c72011-04-01 14:43:32 -07005056 public void acknowledgeFullBackupOrRestore(int token, boolean allow,
Christopher Tate728a1c42011-07-28 18:03:03 -07005057 String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
Christopher Tate4a627c72011-04-01 14:43:32 -07005058 if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
5059 + " allow=" + allow);
5060
5061 // TODO: possibly require not just this signature-only permission, but even
5062 // require that the specific designated confirmation-UI app uid is the caller?
Christopher Tate2efd2db2011-07-19 16:32:49 -07005063 mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
Christopher Tate4a627c72011-04-01 14:43:32 -07005064
5065 long oldId = Binder.clearCallingIdentity();
5066 try {
5067
5068 FullParams params;
5069 synchronized (mFullConfirmations) {
5070 params = mFullConfirmations.get(token);
5071 if (params != null) {
5072 mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
5073 mFullConfirmations.delete(token);
5074
5075 if (allow) {
Christopher Tate4a627c72011-04-01 14:43:32 -07005076 final int verb = params instanceof FullBackupParams
Christopher Tate75a99702011-05-18 16:28:19 -07005077 ? MSG_RUN_FULL_BACKUP
Christopher Tate4a627c72011-04-01 14:43:32 -07005078 : MSG_RUN_FULL_RESTORE;
5079
Christopher Tate728a1c42011-07-28 18:03:03 -07005080 params.observer = observer;
5081 params.curPassword = curPassword;
Christopher Tate32418be2011-10-10 13:51:12 -07005082
5083 boolean isEncrypted;
5084 try {
5085 isEncrypted = (mMountService.getEncryptionState() != MountService.ENCRYPTION_STATE_NONE);
5086 if (isEncrypted) Slog.w(TAG, "Device is encrypted; forcing enc password");
5087 } catch (RemoteException e) {
5088 // couldn't contact the mount service; fail "safe" and assume encryption
5089 Slog.e(TAG, "Unable to contact mount service!");
5090 isEncrypted = true;
5091 }
5092 params.encryptPassword = (isEncrypted) ? curPassword : encPpassword;
Christopher Tate728a1c42011-07-28 18:03:03 -07005093
Christopher Tate75a99702011-05-18 16:28:19 -07005094 if (DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
Christopher Tate4a627c72011-04-01 14:43:32 -07005095 mWakelock.acquire();
5096 Message msg = mBackupHandler.obtainMessage(verb, params);
5097 mBackupHandler.sendMessage(msg);
5098 } else {
5099 Slog.w(TAG, "User rejected full backup/restore operation");
5100 // indicate completion without having actually transferred any data
5101 signalFullBackupRestoreCompletion(params);
5102 }
5103 } else {
5104 Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
5105 }
5106 }
5107 } finally {
5108 Binder.restoreCallingIdentity(oldId);
5109 }
5110 }
5111
Christopher Tate8031a3d2009-07-06 16:36:05 -07005112 // Enable/disable the backup service
Christopher Tate6ef58a12009-06-29 14:56:28 -07005113 public void setBackupEnabled(boolean enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07005114 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate2efd2db2011-07-19 16:32:49 -07005115 "setBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07005116
Joe Onorato8a9b2202010-02-26 18:56:32 -08005117 Slog.i(TAG, "Backup enabled => " + enable);
Christopher Tate4cc86e12009-09-21 19:36:51 -07005118
Christopher Tate6ef58a12009-06-29 14:56:28 -07005119 boolean wasEnabled = mEnabled;
5120 synchronized (this) {
Dianne Hackborncf098292009-07-01 19:55:20 -07005121 Settings.Secure.putInt(mContext.getContentResolver(),
5122 Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
Christopher Tate6ef58a12009-06-29 14:56:28 -07005123 mEnabled = enable;
5124 }
5125
Christopher Tate49401dd2009-07-01 12:34:29 -07005126 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07005127 if (enable && !wasEnabled && mProvisioned) {
Christopher Tate49401dd2009-07-01 12:34:29 -07005128 // if we've just been enabled, start scheduling backup passes
Christopher Tate8031a3d2009-07-06 16:36:05 -07005129 startBackupAlarmsLocked(BACKUP_INTERVAL);
Christopher Tate49401dd2009-07-01 12:34:29 -07005130 } else if (!enable) {
Christopher Tateb6787f22009-07-02 17:40:45 -07005131 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08005132 if (DEBUG) Slog.i(TAG, "Opting out of backup");
Christopher Tate4cc86e12009-09-21 19:36:51 -07005133
Christopher Tateb6787f22009-07-02 17:40:45 -07005134 mAlarmManager.cancel(mRunBackupIntent);
Christopher Tate4cc86e12009-09-21 19:36:51 -07005135
5136 // This also constitutes an opt-out, so we wipe any data for
5137 // this device from the backend. We start that process with
5138 // an alarm in order to guarantee wakelock states.
5139 if (wasEnabled && mProvisioned) {
5140 // NOTE: we currently flush every registered transport, not just
5141 // the currently-active one.
5142 HashSet<String> allTransports;
5143 synchronized (mTransports) {
5144 allTransports = new HashSet<String>(mTransports.keySet());
5145 }
5146 // build the set of transports for which we are posting an init
5147 for (String transport : allTransports) {
5148 recordInitPendingLocked(true, transport);
5149 }
5150 mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
5151 mRunInitIntent);
5152 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07005153 }
5154 }
Christopher Tate49401dd2009-07-01 12:34:29 -07005155 }
Christopher Tate6ef58a12009-06-29 14:56:28 -07005156
Christopher Tatecce9da52010-02-03 15:11:15 -08005157 // Enable/disable automatic restore of app data at install time
5158 public void setAutoRestore(boolean doAutoRestore) {
5159 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate2efd2db2011-07-19 16:32:49 -07005160 "setAutoRestore");
Christopher Tatecce9da52010-02-03 15:11:15 -08005161
Joe Onorato8a9b2202010-02-26 18:56:32 -08005162 Slog.i(TAG, "Auto restore => " + doAutoRestore);
Christopher Tatecce9da52010-02-03 15:11:15 -08005163
5164 synchronized (this) {
5165 Settings.Secure.putInt(mContext.getContentResolver(),
5166 Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
5167 mAutoRestore = doAutoRestore;
5168 }
5169 }
5170
Christopher Tate8031a3d2009-07-06 16:36:05 -07005171 // Mark the backup service as having been provisioned
5172 public void setBackupProvisioned(boolean available) {
5173 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5174 "setBackupProvisioned");
5175
5176 boolean wasProvisioned = mProvisioned;
5177 synchronized (this) {
5178 Settings.Secure.putInt(mContext.getContentResolver(),
5179 Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
5180 mProvisioned = available;
5181 }
5182
5183 synchronized (mQueueLock) {
5184 if (available && !wasProvisioned && mEnabled) {
5185 // we're now good to go, so start the backup alarms
5186 startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
5187 } else if (!available) {
5188 // No longer enabled, so stop running backups
Joe Onorato8a9b2202010-02-26 18:56:32 -08005189 Slog.w(TAG, "Backup service no longer provisioned");
Christopher Tate8031a3d2009-07-06 16:36:05 -07005190 mAlarmManager.cancel(mRunBackupIntent);
5191 }
5192 }
5193 }
5194
5195 private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
Dan Egnorc1c49c02009-10-30 17:35:39 -07005196 // We used to use setInexactRepeating(), but that may be linked to
5197 // backups running at :00 more often than not, creating load spikes.
5198 // Schedule at an exact time for now, and also add a bit of "fuzz".
5199
5200 Random random = new Random();
5201 long when = System.currentTimeMillis() + delayBeforeFirstBackup +
5202 random.nextInt(FUZZ_MILLIS);
5203 mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, when,
5204 BACKUP_INTERVAL + random.nextInt(FUZZ_MILLIS), mRunBackupIntent);
Christopher Tate55f931a2009-09-29 17:17:34 -07005205 mNextBackupPass = when;
Christopher Tate8031a3d2009-07-06 16:36:05 -07005206 }
5207
Christopher Tate6ef58a12009-06-29 14:56:28 -07005208 // Report whether the backup mechanism is currently enabled
5209 public boolean isBackupEnabled() {
Joe Onorato5933a492009-07-23 18:24:08 -04005210 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
Christopher Tate6ef58a12009-06-29 14:56:28 -07005211 return mEnabled; // no need to synchronize just to read it
5212 }
5213
Christopher Tate91717492009-06-26 21:07:13 -07005214 // Report the name of the currently active transport
5215 public String getCurrentTransport() {
Joe Onorato5933a492009-07-23 18:24:08 -04005216 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate4e3e50c2009-07-02 12:14:05 -07005217 "getCurrentTransport");
Christopher Tatec58efa62011-08-01 19:20:14 -07005218 if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
Christopher Tate91717492009-06-26 21:07:13 -07005219 return mCurrentTransport;
Christopher Tateace7f092009-06-15 18:07:25 -07005220 }
5221
Christopher Tate91717492009-06-26 21:07:13 -07005222 // Report all known, available backup transports
5223 public String[] listAllTransports() {
Christopher Tate34ebd0e2009-07-06 15:44:54 -07005224 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
Christopher Tate043dadc2009-06-02 16:11:00 -07005225
Christopher Tate91717492009-06-26 21:07:13 -07005226 String[] list = null;
5227 ArrayList<String> known = new ArrayList<String>();
5228 for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
5229 if (entry.getValue() != null) {
5230 known.add(entry.getKey());
5231 }
5232 }
5233
5234 if (known.size() > 0) {
5235 list = new String[known.size()];
5236 known.toArray(list);
5237 }
5238 return list;
5239 }
5240
5241 // Select which transport to use for the next backup operation. If the given
5242 // name is not one of the available transports, no action is taken and the method
5243 // returns null.
5244 public String selectBackupTransport(String transport) {
Joe Onorato5933a492009-07-23 18:24:08 -04005245 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
Christopher Tate91717492009-06-26 21:07:13 -07005246
5247 synchronized (mTransports) {
5248 String prevTransport = null;
5249 if (mTransports.get(transport) != null) {
5250 prevTransport = mCurrentTransport;
5251 mCurrentTransport = transport;
Dianne Hackborncf098292009-07-01 19:55:20 -07005252 Settings.Secure.putString(mContext.getContentResolver(),
5253 Settings.Secure.BACKUP_TRANSPORT, transport);
Joe Onorato8a9b2202010-02-26 18:56:32 -08005254 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
Christopher Tate91717492009-06-26 21:07:13 -07005255 + " returning " + prevTransport);
5256 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005257 Slog.w(TAG, "Attempt to select unavailable transport " + transport);
Christopher Tate91717492009-06-26 21:07:13 -07005258 }
5259 return prevTransport;
5260 }
Christopher Tate043dadc2009-06-02 16:11:00 -07005261 }
5262
Christopher Tatef5e1c292010-12-08 18:40:26 -08005263 // Supply the configuration Intent for the given transport. If the name is not one
5264 // of the available transports, or if the transport does not supply any configuration
5265 // UI, the method returns null.
5266 public Intent getConfigurationIntent(String transportName) {
5267 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5268 "getConfigurationIntent");
5269
5270 synchronized (mTransports) {
5271 final IBackupTransport transport = mTransports.get(transportName);
5272 if (transport != null) {
5273 try {
5274 final Intent intent = transport.configurationIntent();
Christopher Tatec58efa62011-08-01 19:20:14 -07005275 if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
Christopher Tatef5e1c292010-12-08 18:40:26 -08005276 + intent);
5277 return intent;
5278 } catch (RemoteException e) {
5279 /* fall through to return null */
5280 }
5281 }
5282 }
5283
5284 return null;
5285 }
5286
5287 // Supply the configuration summary string for the given transport. If the name is
5288 // not one of the available transports, or if the transport does not supply any
5289 // summary / destination string, the method can return null.
5290 //
5291 // This string is used VERBATIM as the summary text of the relevant Settings item!
5292 public String getDestinationString(String transportName) {
5293 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate2efd2db2011-07-19 16:32:49 -07005294 "getDestinationString");
Christopher Tatef5e1c292010-12-08 18:40:26 -08005295
5296 synchronized (mTransports) {
5297 final IBackupTransport transport = mTransports.get(transportName);
5298 if (transport != null) {
5299 try {
5300 final String text = transport.currentDestinationString();
Christopher Tatec58efa62011-08-01 19:20:14 -07005301 if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
Christopher Tatef5e1c292010-12-08 18:40:26 -08005302 return text;
5303 } catch (RemoteException e) {
5304 /* fall through to return null */
5305 }
5306 }
5307 }
5308
5309 return null;
5310 }
5311
Christopher Tate043dadc2009-06-02 16:11:00 -07005312 // Callback: a requested backup agent has been instantiated. This should only
5313 // be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07005314 public void agentConnected(String packageName, IBinder agentBinder) {
Christopher Tate043dadc2009-06-02 16:11:00 -07005315 synchronized(mAgentConnectLock) {
5316 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005317 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
Christopher Tate043dadc2009-06-02 16:11:00 -07005318 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
5319 mConnectedAgent = agent;
5320 mConnecting = false;
5321 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005322 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07005323 + " claiming agent connected");
5324 }
5325 mAgentConnectLock.notifyAll();
5326 }
Christopher Tate181fafa2009-05-14 11:12:14 -07005327 }
5328
5329 // Callback: a backup agent has failed to come up, or has unexpectedly quit.
5330 // If the agent failed to come up in the first place, the agentBinder argument
Christopher Tate043dadc2009-06-02 16:11:00 -07005331 // will be null. This should only be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07005332 public void agentDisconnected(String packageName) {
5333 // TODO: handle backup being interrupted
Christopher Tate043dadc2009-06-02 16:11:00 -07005334 synchronized(mAgentConnectLock) {
5335 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
5336 mConnectedAgent = null;
5337 mConnecting = false;
5338 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005339 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate043dadc2009-06-02 16:11:00 -07005340 + " claiming agent disconnected");
5341 }
5342 mAgentConnectLock.notifyAll();
5343 }
Christopher Tate181fafa2009-05-14 11:12:14 -07005344 }
Christopher Tate181fafa2009-05-14 11:12:14 -07005345
Christopher Tate1bb69062010-02-19 17:02:12 -08005346 // An application being installed will need a restore pass, then the Package Manager
5347 // will need to be told when the restore is finished.
5348 public void restoreAtInstall(String packageName, int token) {
5349 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005350 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
Christopher Tate1bb69062010-02-19 17:02:12 -08005351 + " attemping install-time restore");
5352 return;
5353 }
5354
5355 long restoreSet = getAvailableRestoreToken(packageName);
Joe Onorato8a9b2202010-02-26 18:56:32 -08005356 if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
Christopher Tate1bb69062010-02-19 17:02:12 -08005357 + " token=" + Integer.toHexString(token));
5358
Christopher Tatef0872722010-02-25 15:22:48 -08005359 if (mAutoRestore && mProvisioned && restoreSet != 0) {
Christopher Tate1bb69062010-02-19 17:02:12 -08005360 // okay, we're going to attempt a restore of this package from this restore set.
5361 // The eventual message back into the Package Manager to run the post-install
5362 // steps for 'token' will be issued from the restore handling code.
5363
5364 // We can use a synthetic PackageInfo here because:
5365 // 1. We know it's valid, since the Package Manager supplied the name
5366 // 2. Only the packageName field will be used by the restore code
5367 PackageInfo pkg = new PackageInfo();
5368 pkg.packageName = packageName;
5369
5370 mWakelock.acquire();
5371 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
5372 msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
Chris Tate249345b2010-10-29 12:57:04 -07005373 restoreSet, pkg, token, true);
Christopher Tate1bb69062010-02-19 17:02:12 -08005374 mBackupHandler.sendMessage(msg);
5375 } else {
Christopher Tatef0872722010-02-25 15:22:48 -08005376 // Auto-restore disabled or no way to attempt a restore; just tell the Package
5377 // Manager to proceed with the post-install handling for this package.
Joe Onorato8a9b2202010-02-26 18:56:32 -08005378 if (DEBUG) Slog.v(TAG, "No restore set -- skipping restore");
Christopher Tate1bb69062010-02-19 17:02:12 -08005379 try {
5380 mPackageManagerBinder.finishPackageInstall(token);
5381 } catch (RemoteException e) { /* can't happen */ }
5382 }
5383 }
5384
Christopher Tate8c850b72009-06-07 19:33:20 -07005385 // Hand off a restore session
Chris Tate44ab8452010-11-16 15:10:49 -08005386 public IRestoreSession beginRestoreSession(String packageName, String transport) {
5387 if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
5388 + " transport=" + transport);
5389
5390 boolean needPermission = true;
5391 if (transport == null) {
5392 transport = mCurrentTransport;
5393
5394 if (packageName != null) {
5395 PackageInfo app = null;
5396 try {
5397 app = mPackageManager.getPackageInfo(packageName, 0);
5398 } catch (NameNotFoundException nnf) {
5399 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
5400 throw new IllegalArgumentException("Package " + packageName + " not found");
5401 }
5402
5403 if (app.applicationInfo.uid == Binder.getCallingUid()) {
5404 // So: using the current active transport, and the caller has asked
5405 // that its own package will be restored. In this narrow use case
5406 // we do not require the caller to hold the permission.
5407 needPermission = false;
5408 }
5409 }
5410 }
5411
5412 if (needPermission) {
5413 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5414 "beginRestoreSession");
5415 } else {
5416 if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
5417 }
Christopher Tatef68eb502009-06-16 11:02:01 -07005418
5419 synchronized(this) {
5420 if (mActiveRestoreSession != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005421 Slog.d(TAG, "Restore session requested but one already active");
Christopher Tatef68eb502009-06-16 11:02:01 -07005422 return null;
5423 }
Chris Tate44ab8452010-11-16 15:10:49 -08005424 mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
Christopher Tate73a3cb32010-12-13 18:27:26 -08005425 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
Christopher Tatef68eb502009-06-16 11:02:01 -07005426 }
5427 return mActiveRestoreSession;
Christopher Tate8c850b72009-06-07 19:33:20 -07005428 }
Christopher Tate043dadc2009-06-02 16:11:00 -07005429
Christopher Tate73a3cb32010-12-13 18:27:26 -08005430 void clearRestoreSession(ActiveRestoreSession currentSession) {
5431 synchronized(this) {
5432 if (currentSession != mActiveRestoreSession) {
5433 Slog.e(TAG, "ending non-current restore session");
5434 } else {
5435 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
5436 mActiveRestoreSession = null;
5437 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
5438 }
5439 }
5440 }
5441
Christopher Tate44a27902010-01-27 17:15:49 -08005442 // Note that a currently-active backup agent has notified us that it has
5443 // completed the given outstanding asynchronous backup/restore operation.
Christopher Tate8e294d42011-08-31 20:37:12 -07005444 @Override
Christopher Tate44a27902010-01-27 17:15:49 -08005445 public void opComplete(int token) {
Christopher Tate8e294d42011-08-31 20:37:12 -07005446 if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
5447 Operation op = null;
Christopher Tate44a27902010-01-27 17:15:49 -08005448 synchronized (mCurrentOpLock) {
Christopher Tate8e294d42011-08-31 20:37:12 -07005449 op = mCurrentOperations.get(token);
5450 if (op != null) {
5451 op.state = OP_ACKNOWLEDGED;
5452 }
Christopher Tate44a27902010-01-27 17:15:49 -08005453 mCurrentOpLock.notifyAll();
5454 }
Christopher Tate8e294d42011-08-31 20:37:12 -07005455
5456 // The completion callback, if any, is invoked on the handler
5457 if (op != null && op.callback != null) {
5458 Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
5459 mBackupHandler.sendMessage(msg);
5460 }
Christopher Tate44a27902010-01-27 17:15:49 -08005461 }
5462
Christopher Tate9b3905c2009-06-08 15:24:01 -07005463 // ----- Restore session -----
5464
Christopher Tate80202c82010-01-25 19:37:47 -08005465 class ActiveRestoreSession extends IRestoreSession.Stub {
Christopher Tatef68eb502009-06-16 11:02:01 -07005466 private static final String TAG = "RestoreSession";
5467
Chris Tate44ab8452010-11-16 15:10:49 -08005468 private String mPackageName;
Christopher Tate9b3905c2009-06-08 15:24:01 -07005469 private IBackupTransport mRestoreTransport = null;
5470 RestoreSet[] mRestoreSets = null;
Christopher Tate73a3cb32010-12-13 18:27:26 -08005471 boolean mEnded = false;
Christopher Tate9b3905c2009-06-08 15:24:01 -07005472
Chris Tate44ab8452010-11-16 15:10:49 -08005473 ActiveRestoreSession(String packageName, String transport) {
5474 mPackageName = packageName;
Christopher Tate91717492009-06-26 21:07:13 -07005475 mRestoreTransport = getTransport(transport);
Christopher Tate9b3905c2009-06-08 15:24:01 -07005476 }
5477
5478 // --- Binder interface ---
Christopher Tate2d449afe2010-03-29 19:14:24 -07005479 public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
Joe Onorato5933a492009-07-23 18:24:08 -04005480 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
Christopher Tate9bbc21a2009-06-10 20:23:25 -07005481 "getAvailableRestoreSets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07005482 if (observer == null) {
5483 throw new IllegalArgumentException("Observer must not be null");
5484 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07005485
Christopher Tate73a3cb32010-12-13 18:27:26 -08005486 if (mEnded) {
5487 throw new IllegalStateException("Restore session already ended");
5488 }
5489
Christopher Tate1bb69062010-02-19 17:02:12 -08005490 long oldId = Binder.clearCallingIdentity();
Christopher Tatef68eb502009-06-16 11:02:01 -07005491 try {
Christopher Tate43383042009-07-13 15:17:13 -07005492 if (mRestoreTransport == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005493 Slog.w(TAG, "Null transport getting restore sets");
Christopher Tate2d449afe2010-03-29 19:14:24 -07005494 return -1;
Dan Egnor0084da52009-07-29 12:57:16 -07005495 }
Christopher Tate2d449afe2010-03-29 19:14:24 -07005496 // spin off the transport request to our service thread
5497 mWakelock.acquire();
5498 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
5499 new RestoreGetSetsParams(mRestoreTransport, this, observer));
5500 mBackupHandler.sendMessage(msg);
5501 return 0;
Dan Egnor0084da52009-07-29 12:57:16 -07005502 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005503 Slog.e(TAG, "Error in getAvailableRestoreSets", e);
Christopher Tate2d449afe2010-03-29 19:14:24 -07005504 return -1;
Christopher Tate1bb69062010-02-19 17:02:12 -08005505 } finally {
5506 Binder.restoreCallingIdentity(oldId);
Christopher Tatef68eb502009-06-16 11:02:01 -07005507 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07005508 }
5509
Christopher Tate84725812010-02-04 15:52:40 -08005510 public synchronized int restoreAll(long token, IRestoreObserver observer) {
Dan Egnor0084da52009-07-29 12:57:16 -07005511 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5512 "performRestore");
Christopher Tate9bbc21a2009-06-10 20:23:25 -07005513
Chris Tate44ab8452010-11-16 15:10:49 -08005514 if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
Christopher Tatef2c321a2009-08-10 15:43:36 -07005515 + " observer=" + observer);
Joe Onorato9a5e3e12009-07-01 21:04:03 -04005516
Christopher Tate73a3cb32010-12-13 18:27:26 -08005517 if (mEnded) {
5518 throw new IllegalStateException("Restore session already ended");
5519 }
5520
Dan Egnor0084da52009-07-29 12:57:16 -07005521 if (mRestoreTransport == null || mRestoreSets == null) {
Chris Tate44ab8452010-11-16 15:10:49 -08005522 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
5523 return -1;
5524 }
5525
5526 if (mPackageName != null) {
5527 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
Dan Egnor0084da52009-07-29 12:57:16 -07005528 return -1;
5529 }
5530
Christopher Tate21ab6a52009-09-24 18:01:46 -07005531 synchronized (mQueueLock) {
Christopher Tate21ab6a52009-09-24 18:01:46 -07005532 for (int i = 0; i < mRestoreSets.length; i++) {
5533 if (token == mRestoreSets[i].token) {
5534 long oldId = Binder.clearCallingIdentity();
Christopher Tate21ab6a52009-09-24 18:01:46 -07005535 mWakelock.acquire();
5536 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07005537 msg.obj = new RestoreParams(mRestoreTransport, observer, token, true);
Christopher Tate21ab6a52009-09-24 18:01:46 -07005538 mBackupHandler.sendMessage(msg);
5539 Binder.restoreCallingIdentity(oldId);
5540 return 0;
5541 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -07005542 }
5543 }
Christopher Tate0e0b4ae2009-08-10 16:13:47 -07005544
Joe Onorato8a9b2202010-02-26 18:56:32 -08005545 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
Christopher Tate9b3905c2009-06-08 15:24:01 -07005546 return -1;
5547 }
5548
Christopher Tate284f1bb2011-07-07 14:31:18 -07005549 public synchronized int restoreSome(long token, IRestoreObserver observer,
5550 String[] packages) {
5551 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
5552 "performRestore");
5553
5554 if (DEBUG) {
5555 StringBuilder b = new StringBuilder(128);
5556 b.append("restoreSome token=");
5557 b.append(Long.toHexString(token));
5558 b.append(" observer=");
5559 b.append(observer.toString());
5560 b.append(" packages=");
5561 if (packages == null) {
5562 b.append("null");
5563 } else {
5564 b.append('{');
5565 boolean first = true;
5566 for (String s : packages) {
5567 if (!first) {
5568 b.append(", ");
5569 } else first = false;
5570 b.append(s);
5571 }
5572 b.append('}');
5573 }
5574 Slog.d(TAG, b.toString());
5575 }
5576
5577 if (mEnded) {
5578 throw new IllegalStateException("Restore session already ended");
5579 }
5580
5581 if (mRestoreTransport == null || mRestoreSets == null) {
5582 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
5583 return -1;
5584 }
5585
5586 if (mPackageName != null) {
5587 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
5588 return -1;
5589 }
5590
5591 synchronized (mQueueLock) {
5592 for (int i = 0; i < mRestoreSets.length; i++) {
5593 if (token == mRestoreSets[i].token) {
5594 long oldId = Binder.clearCallingIdentity();
5595 mWakelock.acquire();
5596 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
5597 msg.obj = new RestoreParams(mRestoreTransport, observer, token,
5598 packages, true);
5599 mBackupHandler.sendMessage(msg);
5600 Binder.restoreCallingIdentity(oldId);
5601 return 0;
5602 }
5603 }
5604 }
5605
5606 Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
5607 return -1;
5608 }
5609
Christopher Tate84725812010-02-04 15:52:40 -08005610 public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005611 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
Christopher Tate84725812010-02-04 15:52:40 -08005612
Christopher Tate73a3cb32010-12-13 18:27:26 -08005613 if (mEnded) {
5614 throw new IllegalStateException("Restore session already ended");
5615 }
5616
Chris Tate44ab8452010-11-16 15:10:49 -08005617 if (mPackageName != null) {
5618 if (! mPackageName.equals(packageName)) {
5619 Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
5620 + " on session for package " + mPackageName);
5621 return -1;
5622 }
5623 }
5624
Christopher Tate84725812010-02-04 15:52:40 -08005625 PackageInfo app = null;
5626 try {
5627 app = mPackageManager.getPackageInfo(packageName, 0);
5628 } catch (NameNotFoundException nnf) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005629 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
Christopher Tate84725812010-02-04 15:52:40 -08005630 return -1;
5631 }
5632
5633 // If the caller is not privileged and is not coming from the target
5634 // app's uid, throw a permission exception back to the caller.
5635 int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
5636 Binder.getCallingPid(), Binder.getCallingUid());
5637 if ((perm == PackageManager.PERMISSION_DENIED) &&
5638 (app.applicationInfo.uid != Binder.getCallingUid())) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005639 Slog.w(TAG, "restorePackage: bad packageName=" + packageName
Christopher Tate84725812010-02-04 15:52:40 -08005640 + " or calling uid=" + Binder.getCallingUid());
5641 throw new SecurityException("No permission to restore other packages");
5642 }
5643
Christopher Tate7d411a32010-02-26 11:27:08 -08005644 // If the package has no backup agent, we obviously cannot proceed
5645 if (app.applicationInfo.backupAgentName == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005646 Slog.w(TAG, "Asked to restore package " + packageName + " with no agent");
Christopher Tate7d411a32010-02-26 11:27:08 -08005647 return -1;
5648 }
5649
Christopher Tate84725812010-02-04 15:52:40 -08005650 // So far so good; we're allowed to try to restore this package. Now
5651 // check whether there is data for it in the current dataset, falling back
5652 // to the ancestral dataset if not.
Christopher Tate1bb69062010-02-19 17:02:12 -08005653 long token = getAvailableRestoreToken(packageName);
Christopher Tate84725812010-02-04 15:52:40 -08005654
5655 // If we didn't come up with a place to look -- no ancestral dataset and
5656 // the app has never been backed up from this device -- there's nothing
5657 // to do but return failure.
5658 if (token == 0) {
Chris Tate44ab8452010-11-16 15:10:49 -08005659 if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
Christopher Tate84725812010-02-04 15:52:40 -08005660 return -1;
5661 }
5662
5663 // Ready to go: enqueue the restore request and claim success
5664 long oldId = Binder.clearCallingIdentity();
5665 mWakelock.acquire();
5666 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
Chris Tate249345b2010-10-29 12:57:04 -07005667 msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0, false);
Christopher Tate84725812010-02-04 15:52:40 -08005668 mBackupHandler.sendMessage(msg);
5669 Binder.restoreCallingIdentity(oldId);
5670 return 0;
5671 }
5672
Christopher Tate73a3cb32010-12-13 18:27:26 -08005673 // Posted to the handler to tear down a restore session in a cleanly synchronized way
5674 class EndRestoreRunnable implements Runnable {
5675 BackupManagerService mBackupManager;
5676 ActiveRestoreSession mSession;
5677
5678 EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
5679 mBackupManager = manager;
5680 mSession = session;
5681 }
5682
5683 public void run() {
5684 // clean up the session's bookkeeping
5685 synchronized (mSession) {
5686 try {
5687 if (mSession.mRestoreTransport != null) {
5688 mSession.mRestoreTransport.finishRestore();
5689 }
5690 } catch (Exception e) {
5691 Slog.e(TAG, "Error in finishRestore", e);
5692 } finally {
5693 mSession.mRestoreTransport = null;
5694 mSession.mEnded = true;
5695 }
5696 }
5697
5698 // clean up the BackupManagerService side of the bookkeeping
5699 // and cancel any pending timeout message
5700 mBackupManager.clearRestoreSession(mSession);
5701 }
5702 }
5703
Dan Egnor0084da52009-07-29 12:57:16 -07005704 public synchronized void endRestoreSession() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08005705 if (DEBUG) Slog.d(TAG, "endRestoreSession");
Joe Onorato9a5e3e12009-07-01 21:04:03 -04005706
Christopher Tate73a3cb32010-12-13 18:27:26 -08005707 if (mEnded) {
5708 throw new IllegalStateException("Restore session already ended");
Dan Egnor0084da52009-07-29 12:57:16 -07005709 }
5710
Christopher Tate73a3cb32010-12-13 18:27:26 -08005711 mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
Christopher Tate9b3905c2009-06-08 15:24:01 -07005712 }
5713 }
5714
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07005715 @Override
5716 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyeb4cc4922012-04-26 18:17:29 -07005717 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
5718
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08005719 long identityToken = Binder.clearCallingIdentity();
5720 try {
5721 dumpInternal(pw);
5722 } finally {
5723 Binder.restoreCallingIdentity(identityToken);
5724 }
5725 }
5726
5727 private void dumpInternal(PrintWriter pw) {
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07005728 synchronized (mQueueLock) {
Christopher Tate8031a3d2009-07-06 16:36:05 -07005729 pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
Christopher Tate55f931a2009-09-29 17:17:34 -07005730 + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
Christopher Tatec2af5d32010-02-02 15:18:58 -08005731 + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
Christopher Tateae06ed92010-02-25 17:13:28 -08005732 pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
Christopher Tate336a6492011-10-05 16:05:43 -07005733 if (mBackupRunning) pw.println("Backup currently running");
5734 pw.println("Last backup pass started: " + mLastBackupPass
Christopher Tate55f931a2009-09-29 17:17:34 -07005735 + " (now = " + System.currentTimeMillis() + ')');
5736 pw.println(" next scheduled: " + mNextBackupPass);
5737
Christopher Tate91717492009-06-26 21:07:13 -07005738 pw.println("Available transports:");
5739 for (String t : listAllTransports()) {
Dan Egnor852f8e42009-09-30 11:20:45 -07005740 pw.println((t.equals(mCurrentTransport) ? " * " : " ") + t);
5741 try {
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08005742 IBackupTransport transport = getTransport(t);
5743 File dir = new File(mBaseStateDir, transport.transportDirName());
5744 pw.println(" destination: " + transport.currentDestinationString());
5745 pw.println(" intent: " + transport.configurationIntent());
Dan Egnor852f8e42009-09-30 11:20:45 -07005746 for (File f : dir.listFiles()) {
5747 pw.println(" " + f.getName() + " - " + f.length() + " state bytes");
5748 }
Fabrice Di Meglio8aac3ee2011-01-12 18:47:14 -08005749 } catch (Exception e) {
5750 Slog.e(TAG, "Error in transport", e);
Dan Egnor852f8e42009-09-30 11:20:45 -07005751 pw.println(" Error: " + e);
5752 }
Christopher Tate91717492009-06-26 21:07:13 -07005753 }
Christopher Tate55f931a2009-09-29 17:17:34 -07005754
5755 pw.println("Pending init: " + mPendingInits.size());
5756 for (String s : mPendingInits) {
5757 pw.println(" " + s);
5758 }
5759
Christopher Tate6de74ff2012-01-17 15:20:32 -08005760 if (DEBUG_BACKUP_TRACE) {
5761 synchronized (mBackupTrace) {
5762 if (!mBackupTrace.isEmpty()) {
5763 pw.println("Most recent backup trace:");
5764 for (String s : mBackupTrace) {
5765 pw.println(" " + s);
5766 }
5767 }
5768 }
5769 }
5770
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07005771 int N = mBackupParticipants.size();
Christopher Tate55f931a2009-09-29 17:17:34 -07005772 pw.println("Participants:");
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07005773 for (int i=0; i<N; i++) {
5774 int uid = mBackupParticipants.keyAt(i);
5775 pw.print(" uid: ");
5776 pw.println(uid);
Christopher Tatea3d55342012-03-27 13:16:18 -07005777 HashSet<String> participants = mBackupParticipants.valueAt(i);
5778 for (String app: participants) {
5779 pw.println(" " + app);
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07005780 }
5781 }
Christopher Tate55f931a2009-09-29 17:17:34 -07005782
Christopher Tateb49ceb32010-02-08 16:22:24 -08005783 pw.println("Ancestral packages: "
5784 + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
Christopher Tate5923c972010-04-04 17:45:35 -07005785 if (mAncestralPackages != null) {
5786 for (String pkg : mAncestralPackages) {
5787 pw.println(" " + pkg);
5788 }
Christopher Tateb49ceb32010-02-08 16:22:24 -08005789 }
5790
Christopher Tate73e02522009-07-15 14:18:26 -07005791 pw.println("Ever backed up: " + mEverStoredApps.size());
5792 for (String pkg : mEverStoredApps) {
5793 pw.println(" " + pkg);
5794 }
Christopher Tate55f931a2009-09-29 17:17:34 -07005795
5796 pw.println("Pending backup: " + mPendingBackups.size());
Christopher Tate6aa41f42009-06-19 14:14:22 -07005797 for (BackupRequest req : mPendingBackups.values()) {
Christopher Tate6ef58a12009-06-29 14:56:28 -07005798 pw.println(" " + req);
Christopher Tate181fafa2009-05-14 11:12:14 -07005799 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07005800 }
5801 }
Christopher Tate487529a2009-04-29 14:03:25 -07005802}