blob: 75e0e64b1d92758c252c069343195608d0f16aa7 [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;
20import android.app.IActivityManager;
21import android.app.IApplicationThread;
22import android.app.IBackupAgent;
Christopher Tate3799bc22009-05-06 16:13:56 -070023import android.content.BroadcastReceiver;
Dan Egnor87a02bc2009-06-17 02:30:10 -070024import android.content.ComponentName;
Christopher Tate487529a2009-04-29 14:03:25 -070025import android.content.Context;
26import android.content.Intent;
Christopher Tate3799bc22009-05-06 16:13:56 -070027import android.content.IntentFilter;
Dan Egnor87a02bc2009-06-17 02:30:10 -070028import android.content.ServiceConnection;
Christopher Tate181fafa2009-05-14 11:12:14 -070029import android.content.pm.ApplicationInfo;
Christopher Tatec7b31e32009-06-10 15:49:30 -070030import android.content.pm.IPackageDataObserver;
Christopher Tate7b881282009-06-07 13:52:37 -070031import android.content.pm.PackageInfo;
Christopher Tate043dadc2009-06-02 16:11:00 -070032import android.content.pm.PackageManager.NameNotFoundException;
Dan Egnor87a02bc2009-06-17 02:30:10 -070033import android.content.pm.PackageManager;
Christopher Tateabce4e82009-06-18 18:35:32 -070034import android.content.pm.Signature;
Christopher Tate3799bc22009-05-06 16:13:56 -070035import android.net.Uri;
Christopher Tate487529a2009-04-29 14:03:25 -070036import android.os.Binder;
Christopher Tate3799bc22009-05-06 16:13:56 -070037import android.os.Bundle;
Christopher Tate22b87872009-05-04 16:41:53 -070038import android.os.Environment;
Christopher Tate487529a2009-04-29 14:03:25 -070039import android.os.Handler;
40import android.os.IBinder;
41import android.os.Message;
Christopher Tate22b87872009-05-04 16:41:53 -070042import android.os.ParcelFileDescriptor;
Christopher Tate043dadc2009-06-02 16:11:00 -070043import android.os.Process;
Christopher Tate487529a2009-04-29 14:03:25 -070044import android.os.RemoteException;
45import android.util.Log;
46import android.util.SparseArray;
47
48import android.backup.IBackupManager;
Christopher Tate8c850b72009-06-07 19:33:20 -070049import android.backup.IRestoreSession;
Christopher Tate043dadc2009-06-02 16:11:00 -070050import android.backup.BackupManager;
Christopher Tate9b3905c2009-06-08 15:24:01 -070051import android.backup.RestoreSet;
Christopher Tate043dadc2009-06-02 16:11:00 -070052
Christopher Tate9bbc21a2009-06-10 20:23:25 -070053import com.android.internal.backup.LocalTransport;
Christopher Tate043dadc2009-06-02 16:11:00 -070054import com.android.internal.backup.IBackupTransport;
Christopher Tate487529a2009-04-29 14:03:25 -070055
Christopher Tate6785dd82009-06-18 15:58:25 -070056import com.android.server.PackageManagerBackupAgent;
Christopher Tate6aa41f42009-06-19 14:14:22 -070057import com.android.server.PackageManagerBackupAgent.Metadata;
Christopher Tate6785dd82009-06-18 15:58:25 -070058
Christopher Tatecde87f42009-06-12 12:55:53 -070059import java.io.EOFException;
Christopher Tate22b87872009-05-04 16:41:53 -070060import java.io.File;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070061import java.io.FileDescriptor;
Christopher Tate22b87872009-05-04 16:41:53 -070062import java.io.FileNotFoundException;
Christopher Tatec7b31e32009-06-10 15:49:30 -070063import java.io.IOException;
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070064import java.io.PrintWriter;
Christopher Tatecde87f42009-06-12 12:55:53 -070065import java.io.RandomAccessFile;
Christopher Tate487529a2009-04-29 14:03:25 -070066import java.lang.String;
Joe Onorato8ad02812009-05-13 01:41:44 -040067import java.util.ArrayList;
68import java.util.HashMap;
Christopher Tate487529a2009-04-29 14:03:25 -070069import java.util.HashSet;
Christopher Tate181fafa2009-05-14 11:12:14 -070070import java.util.Iterator;
Christopher Tate487529a2009-04-29 14:03:25 -070071import java.util.List;
72
73class BackupManagerService extends IBackupManager.Stub {
74 private static final String TAG = "BackupManagerService";
75 private static final boolean DEBUG = true;
Christopher Tateaa088442009-06-16 18:25:46 -070076
Christopher Tate6785dd82009-06-18 15:58:25 -070077 // Default time to wait after data changes before we back up the data
Joe Onoratob1a7ffe2009-05-06 18:06:21 -070078 private static final long COLLECTION_INTERVAL = 1000;
79 //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
Christopher Tate487529a2009-04-29 14:03:25 -070080
81 private static final int MSG_RUN_BACKUP = 1;
Christopher Tate043dadc2009-06-02 16:11:00 -070082 private static final int MSG_RUN_FULL_BACKUP = 2;
Christopher Tate9bbc21a2009-06-10 20:23:25 -070083 private static final int MSG_RUN_RESTORE = 3;
Christopher Tatec7b31e32009-06-10 15:49:30 -070084
85 // Timeout interval for deciding that a bind or clear-data has taken too long
86 static final long TIMEOUT_INTERVAL = 10 * 1000;
87
Christopher Tate487529a2009-04-29 14:03:25 -070088 private Context mContext;
89 private PackageManager mPackageManager;
Christopher Tate181fafa2009-05-14 11:12:14 -070090 private final IActivityManager mActivityManager;
Christopher Tate487529a2009-04-29 14:03:25 -070091 private final BackupHandler mBackupHandler = new BackupHandler();
92 // map UIDs to the set of backup client services within that UID's app set
Christopher Tate181fafa2009-05-14 11:12:14 -070093 private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
94 = new SparseArray<HashSet<ApplicationInfo>>();
Christopher Tate487529a2009-04-29 14:03:25 -070095 // set of backup services that have pending changes
Christopher Tate46758122009-05-06 11:22:00 -070096 private class BackupRequest {
Christopher Tate181fafa2009-05-14 11:12:14 -070097 public ApplicationInfo appInfo;
Christopher Tate46758122009-05-06 11:22:00 -070098 public boolean fullBackup;
Christopher Tateaa088442009-06-16 18:25:46 -070099
Christopher Tate181fafa2009-05-14 11:12:14 -0700100 BackupRequest(ApplicationInfo app, boolean isFull) {
101 appInfo = app;
Christopher Tate46758122009-05-06 11:22:00 -0700102 fullBackup = isFull;
103 }
Christopher Tate181fafa2009-05-14 11:12:14 -0700104
105 public String toString() {
106 return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
107 }
Christopher Tate46758122009-05-06 11:22:00 -0700108 }
Joe Onorato8ad02812009-05-13 01:41:44 -0400109 // Backups that we haven't started yet.
Christopher Tate181fafa2009-05-14 11:12:14 -0700110 private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
111 = new HashMap<ApplicationInfo,BackupRequest>();
Christopher Tate6785dd82009-06-18 15:58:25 -0700112 // Do we need to back up the package manager metadata on the next pass?
113 private boolean mDoPackageManager;
114 private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
Christopher Tate6aa41f42009-06-19 14:14:22 -0700115
116 // locking around the pending-backup management
Christopher Tate487529a2009-04-29 14:03:25 -0700117 private final Object mQueueLock = new Object();
118
Christopher Tate043dadc2009-06-02 16:11:00 -0700119 // The thread performing the sequence of queued backups binds to each app's agent
120 // in succession. Bind notifications are asynchronously delivered through the
121 // Activity Manager; use this lock object to signal when a requested binding has
122 // completed.
123 private final Object mAgentConnectLock = new Object();
124 private IBackupAgent mConnectedAgent;
125 private volatile boolean mConnecting;
126
Christopher Tatec7b31e32009-06-10 15:49:30 -0700127 // A similar synchronicity mechanism around clearing apps' data for restore
128 private final Object mClearDataLock = new Object();
129 private volatile boolean mClearingData;
130
Christopher Tateaa088442009-06-16 18:25:46 -0700131 // Current active transport & restore session
Christopher Tate043dadc2009-06-02 16:11:00 -0700132 private int mTransportId;
Dan Egnor87a02bc2009-06-17 02:30:10 -0700133 private IBackupTransport mLocalTransport, mGoogleTransport;
Christopher Tatef68eb502009-06-16 11:02:01 -0700134 private RestoreSession mActiveRestoreSession;
Christopher Tate043dadc2009-06-02 16:11:00 -0700135
Christopher Tate22b87872009-05-04 16:41:53 -0700136 private File mStateDir;
Christopher Tatef4172472009-05-05 15:50:03 -0700137 private File mDataDir;
Christopher Tatecde87f42009-06-12 12:55:53 -0700138 private File mJournalDir;
139 private File mJournal;
140 private RandomAccessFile mJournalStream;
Christopher Tateaa088442009-06-16 18:25:46 -0700141
Christopher Tate487529a2009-04-29 14:03:25 -0700142 public BackupManagerService(Context context) {
143 mContext = context;
144 mPackageManager = context.getPackageManager();
Christopher Tate181fafa2009-05-14 11:12:14 -0700145 mActivityManager = ActivityManagerNative.getDefault();
Christopher Tate487529a2009-04-29 14:03:25 -0700146
Christopher Tate22b87872009-05-04 16:41:53 -0700147 // Set up our bookkeeping
Christopher Tatef4172472009-05-05 15:50:03 -0700148 mStateDir = new File(Environment.getDataDirectory(), "backup");
Christopher Tate22b87872009-05-04 16:41:53 -0700149 mStateDir.mkdirs();
Christopher Tatef4172472009-05-05 15:50:03 -0700150 mDataDir = Environment.getDownloadCacheDirectory();
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700151
Christopher Tatecde87f42009-06-12 12:55:53 -0700152 // Set up the backup-request journaling
153 mJournalDir = new File(mStateDir, "pending");
154 mJournalDir.mkdirs();
155 makeJournalLocked(); // okay because no other threads are running yet
156
Christopher Tateabce4e82009-06-18 18:35:32 -0700157 // Build our mapping of uid to backup client services. This implicitly
158 // schedules a backup pass on the Package Manager metadata the first
159 // time anything needs to be backed up.
Christopher Tate3799bc22009-05-06 16:13:56 -0700160 synchronized (mBackupParticipants) {
161 addPackageParticipantsLocked(null);
Christopher Tate487529a2009-04-29 14:03:25 -0700162 }
163
Dan Egnor87a02bc2009-06-17 02:30:10 -0700164 // Set up our transport options and initialize the default transport
165 // TODO: Have transports register themselves somehow?
166 // TODO: Don't create transports that we don't need to?
Joe Onorato5d605dc2009-06-18 18:23:43 -0700167 //mTransportId = BackupManager.TRANSPORT_LOCAL;
Dan Egnor87a02bc2009-06-17 02:30:10 -0700168 mTransportId = BackupManager.TRANSPORT_GOOGLE;
169 mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
170 mGoogleTransport = null;
171
172 // Attach to the Google backup transport.
173 Intent intent = new Intent().setComponent(new ComponentName(
174 "com.google.android.backup",
175 "com.google.android.backup.BackupTransportService"));
176 context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
Christopher Tateaa088442009-06-16 18:25:46 -0700177
Christopher Tatecde87f42009-06-12 12:55:53 -0700178 // Now that we know about valid backup participants, parse any
179 // leftover journal files and schedule a new backup pass
180 parseLeftoverJournals();
181
Christopher Tate3799bc22009-05-06 16:13:56 -0700182 // Register for broadcasts about package install, etc., so we can
183 // update the provider list.
184 IntentFilter filter = new IntentFilter();
185 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
186 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
187 filter.addDataScheme("package");
188 mContext.registerReceiver(mBroadcastReceiver, filter);
189 }
190
Christopher Tatecde87f42009-06-12 12:55:53 -0700191 private void makeJournalLocked() {
192 try {
193 mJournal = File.createTempFile("journal", null, mJournalDir);
194 mJournalStream = new RandomAccessFile(mJournal, "rwd");
195 } catch (IOException e) {
196 Log.e(TAG, "Unable to write backup journals");
197 mJournal = null;
198 mJournalStream = null;
199 }
200 }
201
202 private void parseLeftoverJournals() {
203 if (mJournal != null) {
204 File[] allJournals = mJournalDir.listFiles();
205 for (File f : allJournals) {
206 if (f.compareTo(mJournal) != 0) {
207 // This isn't the current journal, so it must be a leftover. Read
208 // out the package names mentioned there and schedule them for
209 // backup.
210 try {
211 Log.i(TAG, "Found stale backup journal, scheduling:");
212 RandomAccessFile in = new RandomAccessFile(f, "r");
213 while (true) {
214 String packageName = in.readUTF();
215 Log.i(TAG, " + " + packageName);
216 dataChanged(packageName);
217 }
218 } catch (EOFException e) {
219 // no more data; we're done
220 } catch (Exception e) {
221 // can't read it or other error; just skip it
222 } finally {
223 // close/delete the file
224 f.delete();
225 }
226 }
227 }
228 }
229 }
230
Christopher Tate3799bc22009-05-06 16:13:56 -0700231 // ----- Track installation/removal of packages -----
232 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
233 public void onReceive(Context context, Intent intent) {
234 if (DEBUG) Log.d(TAG, "Received broadcast " + intent);
235
236 Uri uri = intent.getData();
237 if (uri == null) {
238 return;
Christopher Tate487529a2009-04-29 14:03:25 -0700239 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700240 String pkgName = uri.getSchemeSpecificPart();
241 if (pkgName == null) {
242 return;
243 }
244
245 String action = intent.getAction();
246 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
247 synchronized (mBackupParticipants) {
248 Bundle extras = intent.getExtras();
249 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
250 // The package was just upgraded
251 updatePackageParticipantsLocked(pkgName);
252 } else {
253 // The package was just added
254 addPackageParticipantsLocked(pkgName);
255 }
256 }
257 }
258 else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
259 Bundle extras = intent.getExtras();
260 if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
261 // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
262 } else {
263 synchronized (mBackupParticipants) {
264 removePackageParticipantsLocked(pkgName);
265 }
266 }
267 }
268 }
269 };
270
Dan Egnor87a02bc2009-06-17 02:30:10 -0700271 // ----- Track connection to GoogleBackupTransport service -----
272 ServiceConnection mGoogleConnection = new ServiceConnection() {
273 public void onServiceConnected(ComponentName name, IBinder service) {
274 if (DEBUG) Log.v(TAG, "Connected to Google transport");
275 mGoogleTransport = IBackupTransport.Stub.asInterface(service);
276 }
277
278 public void onServiceDisconnected(ComponentName name) {
279 if (DEBUG) Log.v(TAG, "Disconnected from Google transport");
280 mGoogleTransport = null;
281 }
282 };
283
Joe Onorato8ad02812009-05-13 01:41:44 -0400284 // ----- Run the actual backup process asynchronously -----
285
Christopher Tate181fafa2009-05-14 11:12:14 -0700286 private class BackupHandler extends Handler {
Joe Onorato8ad02812009-05-13 01:41:44 -0400287 public void handleMessage(Message msg) {
288
289 switch (msg.what) {
290 case MSG_RUN_BACKUP:
Dan Egnor87a02bc2009-06-17 02:30:10 -0700291 {
292 IBackupTransport transport = getTransport(mTransportId);
293 if (transport == null) {
294 Log.v(TAG, "Backup requested but no transport available");
295 break;
296 }
297
Joe Onorato8ad02812009-05-13 01:41:44 -0400298 // snapshot the pending-backup set and work on that
Christopher Tate6aa41f42009-06-19 14:14:22 -0700299 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
Christopher Tatecde87f42009-06-12 12:55:53 -0700300 File oldJournal = mJournal;
Joe Onorato8ad02812009-05-13 01:41:44 -0400301 synchronized (mQueueLock) {
Christopher Tateace7f092009-06-15 18:07:25 -0700302 if (mPendingBackups.size() == 0) {
303 Log.v(TAG, "Backup requested but nothing pending");
304 break;
305 }
306
Christopher Tate6aa41f42009-06-19 14:14:22 -0700307 for (BackupRequest b: mPendingBackups.values()) {
308 queue.add(b);
Joe Onorato8ad02812009-05-13 01:41:44 -0400309 }
Christopher Tate6aa41f42009-06-19 14:14:22 -0700310 Log.v(TAG, "clearing pending backups");
311 mPendingBackups.clear();
Christopher Tateace7f092009-06-15 18:07:25 -0700312
313 // Start a new backup-queue journal file too
Christopher Tatecde87f42009-06-12 12:55:53 -0700314 if (mJournalStream != null) {
315 try {
316 mJournalStream.close();
317 } catch (IOException e) {
318 // don't need to do anything
319 }
320 makeJournalLocked();
321 }
322
323 // At this point, we have started a new journal file, and the old
324 // file identity is being passed to the backup processing thread.
325 // When it completes successfully, that old journal file will be
326 // deleted. If we crash prior to that, the old journal is parsed
327 // at next boot and the journaled requests fulfilled.
Joe Onorato8ad02812009-05-13 01:41:44 -0400328 }
Dan Egnor87a02bc2009-06-17 02:30:10 -0700329
Christopher Tate6aa41f42009-06-19 14:14:22 -0700330 (new PerformBackupThread(transport, queue, oldJournal)).start();
Christopher Tate043dadc2009-06-02 16:11:00 -0700331 break;
Dan Egnor87a02bc2009-06-17 02:30:10 -0700332 }
Christopher Tate043dadc2009-06-02 16:11:00 -0700333
334 case MSG_RUN_FULL_BACKUP:
Joe Onorato8ad02812009-05-13 01:41:44 -0400335 break;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700336
337 case MSG_RUN_RESTORE:
338 {
339 int token = msg.arg1;
340 IBackupTransport transport = (IBackupTransport)msg.obj;
Dan Egnor87a02bc2009-06-17 02:30:10 -0700341 (new PerformRestoreThread(transport, token)).start();
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700342 break;
343 }
Joe Onorato8ad02812009-05-13 01:41:44 -0400344 }
345 }
Joe Onorato8ad02812009-05-13 01:41:44 -0400346 }
347
Christopher Tate181fafa2009-05-14 11:12:14 -0700348 // Add the backup agents in the given package to our set of known backup participants.
349 // If 'packageName' is null, adds all backup agents in the whole system.
Christopher Tate3799bc22009-05-06 16:13:56 -0700350 void addPackageParticipantsLocked(String packageName) {
Christopher Tate181fafa2009-05-14 11:12:14 -0700351 // Look for apps that define the android:backupAgent attribute
Christopher Tate043dadc2009-06-02 16:11:00 -0700352 if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
Christopher Tate181fafa2009-05-14 11:12:14 -0700353 List<ApplicationInfo> targetApps = allAgentApps();
354 addPackageParticipantsLockedInner(packageName, targetApps);
Christopher Tate3799bc22009-05-06 16:13:56 -0700355 }
356
Christopher Tate181fafa2009-05-14 11:12:14 -0700357 private void addPackageParticipantsLockedInner(String packageName,
358 List<ApplicationInfo> targetApps) {
359 if (DEBUG) {
360 Log.v(TAG, "Adding " + targetApps.size() + " backup participants:");
361 for (ApplicationInfo a : targetApps) {
362 Log.v(TAG, " " + a + " agent=" + a.backupAgentName);
363 }
364 }
365
366 for (ApplicationInfo app : targetApps) {
367 if (packageName == null || app.packageName.equals(packageName)) {
368 int uid = app.uid;
369 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -0700370 if (set == null) {
Christopher Tate181fafa2009-05-14 11:12:14 -0700371 set = new HashSet<ApplicationInfo>();
Christopher Tate3799bc22009-05-06 16:13:56 -0700372 mBackupParticipants.put(uid, set);
373 }
Christopher Tate181fafa2009-05-14 11:12:14 -0700374 set.add(app);
Christopher Tate6785dd82009-06-18 15:58:25 -0700375 backUpPackageManagerData();
Christopher Tate3799bc22009-05-06 16:13:56 -0700376 }
Christopher Tate487529a2009-04-29 14:03:25 -0700377 }
378 }
379
Christopher Tate6785dd82009-06-18 15:58:25 -0700380 // Remove the given package's entry from our known active set. If
381 // 'packageName' is null, *all* participating apps will be removed.
Christopher Tate3799bc22009-05-06 16:13:56 -0700382 void removePackageParticipantsLocked(String packageName) {
Christopher Tate043dadc2009-06-02 16:11:00 -0700383 if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
Christopher Tate181fafa2009-05-14 11:12:14 -0700384 List<ApplicationInfo> allApps = null;
385 if (packageName != null) {
386 allApps = new ArrayList<ApplicationInfo>();
387 try {
388 ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
389 allApps.add(app);
390 } catch (Exception e) {
391 // just skip it
392 }
393 } else {
394 // all apps with agents
395 allApps = allAgentApps();
396 }
397 removePackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -0700398 }
399
Joe Onorato8ad02812009-05-13 01:41:44 -0400400 private void removePackageParticipantsLockedInner(String packageName,
Christopher Tate181fafa2009-05-14 11:12:14 -0700401 List<ApplicationInfo> agents) {
Christopher Tate043dadc2009-06-02 16:11:00 -0700402 if (DEBUG) {
403 Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
404 + ") removing " + agents.size() + " entries");
405 for (ApplicationInfo a : agents) {
406 Log.v(TAG, " - " + a);
407 }
408 }
Christopher Tate181fafa2009-05-14 11:12:14 -0700409 for (ApplicationInfo app : agents) {
410 if (packageName == null || app.packageName.equals(packageName)) {
411 int uid = app.uid;
412 HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
Christopher Tate3799bc22009-05-06 16:13:56 -0700413 if (set != null) {
Christopher Tatecd4ff2e2009-06-05 13:57:54 -0700414 // Find the existing entry with the same package name, and remove it.
415 // We can't just remove(app) because the instances are different.
416 for (ApplicationInfo entry: set) {
417 if (entry.packageName.equals(app.packageName)) {
418 set.remove(entry);
Christopher Tate6785dd82009-06-18 15:58:25 -0700419 backUpPackageManagerData();
Christopher Tatecd4ff2e2009-06-05 13:57:54 -0700420 break;
421 }
422 }
Christopher Tate3799bc22009-05-06 16:13:56 -0700423 if (set.size() == 0) {
Christopher Tate043dadc2009-06-02 16:11:00 -0700424 mBackupParticipants.delete(uid); }
Christopher Tate3799bc22009-05-06 16:13:56 -0700425 }
426 }
427 }
428 }
429
Christopher Tate181fafa2009-05-14 11:12:14 -0700430 // Returns the set of all applications that define an android:backupAgent attribute
431 private List<ApplicationInfo> allAgentApps() {
Christopher Tate6785dd82009-06-18 15:58:25 -0700432 // !!! TODO: cache this and regenerate only when necessary
Christopher Tate181fafa2009-05-14 11:12:14 -0700433 List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
434 int N = allApps.size();
435 if (N > 0) {
436 for (int a = N-1; a >= 0; a--) {
437 ApplicationInfo app = allApps.get(a);
Christopher Tatedf01dea2009-06-09 20:45:02 -0700438 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
439 || app.backupAgentName == null) {
Christopher Tate181fafa2009-05-14 11:12:14 -0700440 allApps.remove(a);
441 }
442 }
443 }
444 return allApps;
445 }
Christopher Tateaa088442009-06-16 18:25:46 -0700446
Christopher Tate3799bc22009-05-06 16:13:56 -0700447 // Reset the given package's known backup participants. Unlike add/remove, the update
448 // action cannot be passed a null package name.
449 void updatePackageParticipantsLocked(String packageName) {
450 if (packageName == null) {
451 Log.e(TAG, "updatePackageParticipants called with null package name");
452 return;
453 }
Christopher Tate043dadc2009-06-02 16:11:00 -0700454 if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
Christopher Tate3799bc22009-05-06 16:13:56 -0700455
456 // brute force but small code size
Christopher Tate181fafa2009-05-14 11:12:14 -0700457 List<ApplicationInfo> allApps = allAgentApps();
458 removePackageParticipantsLockedInner(packageName, allApps);
459 addPackageParticipantsLockedInner(packageName, allApps);
Christopher Tate3799bc22009-05-06 16:13:56 -0700460 }
461
Christopher Tate6785dd82009-06-18 15:58:25 -0700462 private void backUpPackageManagerData() {
463 // No need to schedule a backup just for the metadata; just piggyback on
464 // the next actual data backup.
465 synchronized(this) {
466 mDoPackageManager = true;
467 }
468 }
469
470 // The queue lock should be held when scheduling a backup pass
471 private void scheduleBackupPassLocked(long timeFromNowMillis) {
472 mBackupHandler.removeMessages(MSG_RUN_BACKUP);
473 mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis);
474 }
475
Dan Egnor87a02bc2009-06-17 02:30:10 -0700476 // Return the given transport
477 private IBackupTransport getTransport(int transportID) {
Christopher Tate8c850b72009-06-07 19:33:20 -0700478 switch (transportID) {
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700479 case BackupManager.TRANSPORT_LOCAL:
Christopher Tate6785dd82009-06-18 15:58:25 -0700480 Log.v(TAG, "Supplying local transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -0700481 return mLocalTransport;
Christopher Tate8c850b72009-06-07 19:33:20 -0700482
483 case BackupManager.TRANSPORT_GOOGLE:
Christopher Tate6785dd82009-06-18 15:58:25 -0700484 Log.v(TAG, "Supplying Google transport");
Dan Egnor87a02bc2009-06-17 02:30:10 -0700485 return mGoogleTransport;
Christopher Tate8c850b72009-06-07 19:33:20 -0700486
487 default:
Christopher Tatef68eb502009-06-16 11:02:01 -0700488 Log.e(TAG, "Asked for unknown transport " + transportID);
Dan Egnor87a02bc2009-06-17 02:30:10 -0700489 return null;
Christopher Tate8c850b72009-06-07 19:33:20 -0700490 }
Christopher Tate8c850b72009-06-07 19:33:20 -0700491 }
492
Christopher Tatedf01dea2009-06-09 20:45:02 -0700493 // fire off a backup agent, blocking until it attaches or times out
494 IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
495 IBackupAgent agent = null;
496 synchronized(mAgentConnectLock) {
497 mConnecting = true;
498 mConnectedAgent = null;
499 try {
500 if (mActivityManager.bindBackupAgent(app, mode)) {
501 Log.d(TAG, "awaiting agent for " + app);
502
503 // success; wait for the agent to arrive
Christopher Tatec7b31e32009-06-10 15:49:30 -0700504 // only wait 10 seconds for the clear data to happen
505 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
506 while (mConnecting && mConnectedAgent == null
507 && (System.currentTimeMillis() < timeoutMark)) {
Christopher Tatedf01dea2009-06-09 20:45:02 -0700508 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -0700509 mAgentConnectLock.wait(5000);
Christopher Tatedf01dea2009-06-09 20:45:02 -0700510 } catch (InterruptedException e) {
Christopher Tatec7b31e32009-06-10 15:49:30 -0700511 // just bail
Christopher Tatedf01dea2009-06-09 20:45:02 -0700512 return null;
513 }
514 }
515
516 // if we timed out with no connect, abort and move on
517 if (mConnecting == true) {
518 Log.w(TAG, "Timeout waiting for agent " + app);
519 return null;
520 }
521 agent = mConnectedAgent;
522 }
523 } catch (RemoteException e) {
524 // can't happen
525 }
526 }
527 return agent;
528 }
529
Christopher Tatec7b31e32009-06-10 15:49:30 -0700530 // clear an application's data, blocking until the operation completes or times out
531 void clearApplicationDataSynchronous(String packageName) {
532 ClearDataObserver observer = new ClearDataObserver();
533
534 synchronized(mClearDataLock) {
535 mClearingData = true;
536 mPackageManager.clearApplicationUserData(packageName, observer);
537
538 // only wait 10 seconds for the clear data to happen
539 long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
540 while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
541 try {
542 mClearDataLock.wait(5000);
543 } catch (InterruptedException e) {
544 // won't happen, but still.
545 mClearingData = false;
546 }
547 }
548 }
549 }
550
551 class ClearDataObserver extends IPackageDataObserver.Stub {
552 public void onRemoveCompleted(String packageName, boolean succeeded)
553 throws android.os.RemoteException {
554 synchronized(mClearDataLock) {
555 mClearingData = false;
Christopher Tatef68eb502009-06-16 11:02:01 -0700556 mClearDataLock.notifyAll();
Christopher Tatec7b31e32009-06-10 15:49:30 -0700557 }
558 }
559 }
560
Christopher Tate043dadc2009-06-02 16:11:00 -0700561 // ----- Back up a set of applications via a worker thread -----
562
563 class PerformBackupThread extends Thread {
564 private static final String TAG = "PerformBackupThread";
Christopher Tateaa088442009-06-16 18:25:46 -0700565 IBackupTransport mTransport;
Christopher Tate043dadc2009-06-02 16:11:00 -0700566 ArrayList<BackupRequest> mQueue;
Christopher Tatecde87f42009-06-12 12:55:53 -0700567 File mJournal;
Christopher Tate043dadc2009-06-02 16:11:00 -0700568
Christopher Tateaa088442009-06-16 18:25:46 -0700569 public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue,
Christopher Tatecde87f42009-06-12 12:55:53 -0700570 File journal) {
Christopher Tateaa088442009-06-16 18:25:46 -0700571 mTransport = transport;
Christopher Tate043dadc2009-06-02 16:11:00 -0700572 mQueue = queue;
Christopher Tatecde87f42009-06-12 12:55:53 -0700573 mJournal = journal;
Christopher Tate043dadc2009-06-02 16:11:00 -0700574 }
575
576 @Override
577 public void run() {
Christopher Tate043dadc2009-06-02 16:11:00 -0700578 if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
579
Christopher Tatedf01dea2009-06-09 20:45:02 -0700580 // start up the transport
581 try {
Christopher Tateaa088442009-06-16 18:25:46 -0700582 mTransport.startSession();
Christopher Tatedf01dea2009-06-09 20:45:02 -0700583 } catch (Exception e) {
584 Log.e(TAG, "Error session transport");
585 e.printStackTrace();
586 return;
587 }
588
Christopher Tate6785dd82009-06-18 15:58:25 -0700589 // The transport is up and running. First, back up the package manager
590 // metadata if necessary
591 boolean doPackageManager;
592 synchronized (BackupManagerService.this) {
593 doPackageManager = mDoPackageManager;
594 mDoPackageManager = false;
595 }
596 if (doPackageManager) {
597 // The package manager doesn't have a proper <application> etc, but since
598 // it's running here in the system process we can just set up its agent
599 // directly and use a synthetic BackupRequest.
600 if (DEBUG) Log.i(TAG, "Running PM backup pass as well");
601
602 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
603 mPackageManager, allAgentApps());
604 BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
605 pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
606 processOneBackup(pmRequest,
607 IBackupAgent.Stub.asInterface(pmAgent.onBind()),
608 mTransport);
609 }
610
611 // Now run all the backups in our queue
Christopher Tateaa088442009-06-16 18:25:46 -0700612 doQueuedBackups(mTransport);
Christopher Tate043dadc2009-06-02 16:11:00 -0700613
614 // Finally, tear down the transport
615 try {
Christopher Tateaa088442009-06-16 18:25:46 -0700616 mTransport.endSession();
Christopher Tate043dadc2009-06-02 16:11:00 -0700617 } catch (Exception e) {
618 Log.e(TAG, "Error ending transport");
619 e.printStackTrace();
620 }
Christopher Tatecde87f42009-06-12 12:55:53 -0700621
622 if (!mJournal.delete()) {
623 Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath());
624 }
Christopher Tate043dadc2009-06-02 16:11:00 -0700625 }
626
627 private void doQueuedBackups(IBackupTransport transport) {
628 for (BackupRequest request : mQueue) {
Christopher Tatedf01dea2009-06-09 20:45:02 -0700629 Log.d(TAG, "starting agent for backup of " + request);
Christopher Tate043dadc2009-06-02 16:11:00 -0700630
631 IBackupAgent agent = null;
632 int mode = (request.fullBackup)
633 ? IApplicationThread.BACKUP_MODE_FULL
634 : IApplicationThread.BACKUP_MODE_INCREMENTAL;
635 try {
Christopher Tatedf01dea2009-06-09 20:45:02 -0700636 agent = bindToAgentSynchronous(request.appInfo, mode);
637 if (agent != null) {
638 processOneBackup(request, agent, transport);
Christopher Tate043dadc2009-06-02 16:11:00 -0700639 }
Christopher Tatedf01dea2009-06-09 20:45:02 -0700640
641 // unbind even on timeout, just in case
642 mActivityManager.unbindBackupAgent(request.appInfo);
Christopher Tate043dadc2009-06-02 16:11:00 -0700643 } catch (SecurityException ex) {
644 // Try for the next one.
Christopher Tatec7b31e32009-06-10 15:49:30 -0700645 Log.d(TAG, "error in bind/backup", ex);
Christopher Tate043dadc2009-06-02 16:11:00 -0700646 } catch (RemoteException e) {
Christopher Tatec7b31e32009-06-10 15:49:30 -0700647 Log.v(TAG, "bind/backup threw");
648 e.printStackTrace();
Christopher Tate043dadc2009-06-02 16:11:00 -0700649 }
Christopher Tatedf01dea2009-06-09 20:45:02 -0700650
Christopher Tate043dadc2009-06-02 16:11:00 -0700651 }
652 }
Christopher Tatec7b31e32009-06-10 15:49:30 -0700653
654 void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
655 final String packageName = request.appInfo.packageName;
656 Log.d(TAG, "processOneBackup doBackup() on " + packageName);
657
658 try {
659 // Look up the package info & signatures. This is first so that if it
660 // throws an exception, there's no file setup yet that would need to
661 // be unraveled.
Christopher Tateabce4e82009-06-18 18:35:32 -0700662 PackageInfo packInfo;
663 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
664 // The metadata 'package' is synthetic
665 packInfo = new PackageInfo();
666 packInfo.packageName = packageName;
667 } else {
668 packInfo = mPackageManager.getPackageInfo(packageName,
Christopher Tatec7b31e32009-06-10 15:49:30 -0700669 PackageManager.GET_SIGNATURES);
Christopher Tateabce4e82009-06-18 18:35:32 -0700670 }
Christopher Tatec7b31e32009-06-10 15:49:30 -0700671
672 // !!! TODO: get the state file dir from the transport
673 File savedStateName = new File(mStateDir, packageName);
674 File backupDataName = new File(mDataDir, packageName + ".data");
675 File newStateName = new File(mStateDir, packageName + ".new");
676
677 // In a full backup, we pass a null ParcelFileDescriptor as
678 // the saved-state "file"
679 ParcelFileDescriptor savedState = (request.fullBackup) ? null
680 : ParcelFileDescriptor.open(savedStateName,
681 ParcelFileDescriptor.MODE_READ_ONLY |
682 ParcelFileDescriptor.MODE_CREATE);
683
684 backupDataName.delete();
685 ParcelFileDescriptor backupData =
686 ParcelFileDescriptor.open(backupDataName,
687 ParcelFileDescriptor.MODE_READ_WRITE |
688 ParcelFileDescriptor.MODE_CREATE);
689
690 newStateName.delete();
691 ParcelFileDescriptor newState =
692 ParcelFileDescriptor.open(newStateName,
693 ParcelFileDescriptor.MODE_READ_WRITE |
694 ParcelFileDescriptor.MODE_CREATE);
695
696 // Run the target's backup pass
697 boolean success = false;
698 try {
699 agent.doBackup(savedState, backupData, newState);
700 success = true;
701 } finally {
702 if (savedState != null) {
703 savedState.close();
704 }
705 backupData.close();
706 newState.close();
707 }
708
709 // Now propagate the newly-backed-up data to the transport
710 if (success) {
711 if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
712 backupData =
713 ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
714 int error = transport.performBackup(packInfo, backupData);
715
716 // !!! TODO: After successful transport, delete the now-stale data
717 // and juggle the files so that next time the new state is passed
718 //backupDataName.delete();
719 newStateName.renameTo(savedStateName);
720 }
721 } catch (NameNotFoundException e) {
722 Log.e(TAG, "Package not found on backup: " + packageName);
723 } catch (FileNotFoundException fnf) {
724 Log.w(TAG, "File not found on backup: ");
725 fnf.printStackTrace();
726 } catch (RemoteException e) {
727 Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
728 e.printStackTrace();
729 } catch (Exception e) {
730 Log.w(TAG, "Final exception guard in backup: ");
731 e.printStackTrace();
732 }
733 }
Christopher Tate043dadc2009-06-02 16:11:00 -0700734 }
735
Christopher Tatedf01dea2009-06-09 20:45:02 -0700736
737 // ----- Restore handling -----
738
739 // Is the given package restorable on this device? Returns the on-device app's
740 // ApplicationInfo struct if it is; null if not.
741 //
742 // !!! TODO: also consider signatures
Christopher Tatec7b31e32009-06-10 15:49:30 -0700743 PackageInfo isRestorable(PackageInfo packageInfo) {
Christopher Tatedf01dea2009-06-09 20:45:02 -0700744 if (packageInfo.packageName != null) {
745 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -0700746 PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
Christopher Tatedf01dea2009-06-09 20:45:02 -0700747 PackageManager.GET_SIGNATURES);
Christopher Tatec7b31e32009-06-10 15:49:30 -0700748 if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
Christopher Tatedf01dea2009-06-09 20:45:02 -0700749 return app;
750 }
751 } catch (Exception e) {
752 // doesn't exist on this device, or other error -- just ignore it.
753 }
754 }
755 return null;
756 }
757
Christopher Tateabce4e82009-06-18 18:35:32 -0700758 private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
Christopher Tate20efdf6b2009-06-18 19:41:36 -0700759 // Allow unsigned apps, but not signed on one device and unsigned on the other
760 // !!! TODO: is this the right policy?
Christopher Tate6aa41f42009-06-19 14:14:22 -0700761 if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs
762 + " device=" + deviceSigs);
Christopher Tate20efdf6b2009-06-18 19:41:36 -0700763 if ((storedSigs == null || storedSigs.length == 0)
764 && (deviceSigs == null || deviceSigs.length == 0)) {
765 return true;
766 }
767 if (storedSigs == null || deviceSigs == null) {
768 return false;
769 }
770
Christopher Tateabce4e82009-06-18 18:35:32 -0700771 // !!! TODO: this demands that every stored signature match one
772 // that is present on device, and does not demand the converse.
773 // Is this this right policy?
774 int nStored = storedSigs.length;
775 int nDevice = deviceSigs.length;
776
777 for (int i=0; i < nStored; i++) {
778 boolean match = false;
779 for (int j=0; j < nDevice; j++) {
780 if (storedSigs[i].equals(deviceSigs[j])) {
781 match = true;
782 break;
783 }
784 }
785 if (!match) {
786 return false;
787 }
788 }
789 return true;
790 }
791
Christopher Tatedf01dea2009-06-09 20:45:02 -0700792 class PerformRestoreThread extends Thread {
793 private IBackupTransport mTransport;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700794 private int mToken;
Christopher Tatec7b31e32009-06-10 15:49:30 -0700795 private RestoreSet mImage;
Christopher Tatedf01dea2009-06-09 20:45:02 -0700796
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700797 PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
Christopher Tatedf01dea2009-06-09 20:45:02 -0700798 mTransport = transport;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700799 mToken = restoreSetToken;
Christopher Tatedf01dea2009-06-09 20:45:02 -0700800 }
801
802 @Override
803 public void run() {
Christopher Tate6aa41f42009-06-19 14:14:22 -0700804 if (DEBUG) Log.v(TAG, "Beginning restore process");
Christopher Tatedf01dea2009-06-09 20:45:02 -0700805 /**
806 * Restore sequence:
807 *
808 * 1. start up the transport session
809 * 2. get the restore set description for our identity
810 * 3. for each app in the restore set:
811 * 3.a. if it's restorable on this device, add it to the restore queue
812 * 4. for each app in the restore queue:
Christopher Tatec7b31e32009-06-10 15:49:30 -0700813 * 4.a. clear the app data
Christopher Tatedf01dea2009-06-09 20:45:02 -0700814 * 4.b. get the restore data for the app from the transport
815 * 4.c. launch the backup agent for the app
816 * 4.d. agent.doRestore() with the data from the server
817 * 4.e. unbind the agent [and kill the app?]
818 * 5. shut down the transport
819 */
820
821 int err = -1;
822 try {
823 err = mTransport.startSession();
824 } catch (Exception e) {
825 Log.e(TAG, "Error starting transport for restore");
826 e.printStackTrace();
827 }
828
829 if (err == 0) {
830 // build the set of apps to restore
831 try {
832 RestoreSet[] images = mTransport.getAvailableRestoreSets();
833 if (images.length > 0) {
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700834 // !!! TODO: pick out the set for this token
Christopher Tatec7b31e32009-06-10 15:49:30 -0700835 mImage = images[0];
Christopher Tatedf01dea2009-06-09 20:45:02 -0700836
Christopher Tateabce4e82009-06-18 18:35:32 -0700837 // Pull the Package Manager metadata from the restore set first
838 PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
839 mPackageManager, allAgentApps());
840 PackageInfo pmApp = new PackageInfo();
841 pmApp.packageName = PACKAGE_MANAGER_SENTINEL;
842 processOneRestore(pmApp, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
843
Christopher Tatedf01dea2009-06-09 20:45:02 -0700844 // build the set of apps we will attempt to restore
Christopher Tatec7b31e32009-06-10 15:49:30 -0700845 PackageInfo[] packages = mTransport.getAppSet(mImage.token);
846 HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>();
Christopher Tatedf01dea2009-06-09 20:45:02 -0700847 for (PackageInfo pkg: packages) {
Christopher Tatec7b31e32009-06-10 15:49:30 -0700848 // get the real PackageManager idea of the package
849 PackageInfo app = isRestorable(pkg);
Christopher Tatedf01dea2009-06-09 20:45:02 -0700850 if (app != null) {
Christopher Tateabce4e82009-06-18 18:35:32 -0700851 // Validate against the backed-up signature block, too
Christopher Tate6aa41f42009-06-19 14:14:22 -0700852 Metadata info = pmAgent.getRestoredMetadata(app.packageName);
853 if (app.versionCode >= info.versionCode) {
854 if (DEBUG) Log.v(TAG, "Restore version " + info.versionCode
855 + " compatible with app version " + app.versionCode);
856 if (signaturesMatch(info.signatures, app.signatures)) {
857 appsToRestore.add(app);
858 } else {
859 Log.w(TAG, "Sig mismatch restoring " + app.packageName);
860 }
Christopher Tateabce4e82009-06-18 18:35:32 -0700861 } else {
Christopher Tate6aa41f42009-06-19 14:14:22 -0700862 Log.i(TAG, "Restore set for " + app.packageName
863 + " is too new [" + info.versionCode
864 + "] for installed app version " + app.versionCode);
Christopher Tateabce4e82009-06-18 18:35:32 -0700865 }
Christopher Tatedf01dea2009-06-09 20:45:02 -0700866 }
867 }
868
869 // now run the restore queue
870 doQueuedRestores(appsToRestore);
871 }
872 } catch (RemoteException e) {
873 // can't happen; transports run locally
874 }
875
876 // done; shut down the transport
877 try {
878 mTransport.endSession();
879 } catch (Exception e) {
880 Log.e(TAG, "Error ending transport for restore");
881 e.printStackTrace();
882 }
883 }
884
885 // even if the initial session startup failed, report that we're done here
886 }
887
888 // restore each app in the queue
Christopher Tatec7b31e32009-06-10 15:49:30 -0700889 void doQueuedRestores(HashSet<PackageInfo> appsToRestore) {
890 for (PackageInfo app : appsToRestore) {
Christopher Tatedf01dea2009-06-09 20:45:02 -0700891 Log.d(TAG, "starting agent for restore of " + app);
892
Christopher Tatedf01dea2009-06-09 20:45:02 -0700893 try {
Christopher Tatec7b31e32009-06-10 15:49:30 -0700894 // Remove the app's data first
895 clearApplicationDataSynchronous(app.packageName);
896
897 // Now perform the restore into the clean app
898 IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
899 IApplicationThread.BACKUP_MODE_RESTORE);
Christopher Tatedf01dea2009-06-09 20:45:02 -0700900 if (agent != null) {
901 processOneRestore(app, agent);
902 }
903
904 // unbind even on timeout, just in case
Christopher Tatec7b31e32009-06-10 15:49:30 -0700905 mActivityManager.unbindBackupAgent(app.applicationInfo);
Christopher Tatedf01dea2009-06-09 20:45:02 -0700906 } catch (SecurityException ex) {
907 // Try for the next one.
908 Log.d(TAG, "error in bind", ex);
909 } catch (RemoteException e) {
910 // can't happen
911 }
912
913 }
914 }
915
Christopher Tatec7b31e32009-06-10 15:49:30 -0700916 // Do the guts of a restore of one application, derived from the 'mImage'
917 // restore set via the 'mTransport' transport.
918 void processOneRestore(PackageInfo app, IBackupAgent agent) {
Christopher Tatedf01dea2009-06-09 20:45:02 -0700919 // !!! TODO: actually run the restore through mTransport
Christopher Tatec7b31e32009-06-10 15:49:30 -0700920 final String packageName = app.packageName;
921
922 // !!! TODO: get the dirs from the transport
923 File backupDataName = new File(mDataDir, packageName + ".restore");
924 backupDataName.delete();
925 try {
926 ParcelFileDescriptor backupData =
927 ParcelFileDescriptor.open(backupDataName,
928 ParcelFileDescriptor.MODE_READ_WRITE |
929 ParcelFileDescriptor.MODE_CREATE);
930
931 // Run the transport's restore pass
932 // Run the target's backup pass
933 int err = -1;
934 try {
935 err = mTransport.getRestoreData(mImage.token, app, backupData);
936 } catch (RemoteException e) {
937 // can't happen
938 } finally {
939 backupData.close();
940 }
941
942 // Okay, we have the data. Now have the agent do the restore.
943 File newStateName = new File(mStateDir, packageName + ".new");
944 ParcelFileDescriptor newState =
945 ParcelFileDescriptor.open(newStateName,
946 ParcelFileDescriptor.MODE_READ_WRITE |
947 ParcelFileDescriptor.MODE_CREATE);
948
949 backupData = ParcelFileDescriptor.open(backupDataName,
950 ParcelFileDescriptor.MODE_READ_ONLY);
951
952 boolean success = false;
953 try {
954 agent.doRestore(backupData, newState);
955 success = true;
956 } catch (Exception e) {
957 Log.e(TAG, "Restore failed for " + packageName);
958 e.printStackTrace();
959 } finally {
960 newState.close();
961 backupData.close();
962 }
963
964 // if everything went okay, remember the recorded state now
965 if (success) {
966 File savedStateName = new File(mStateDir, packageName);
967 newStateName.renameTo(savedStateName);
968 }
969 } catch (FileNotFoundException fnfe) {
970 Log.v(TAG, "Couldn't open file for restore: " + fnfe);
971 } catch (IOException ioe) {
972 Log.e(TAG, "Unable to process restore file: " + ioe);
973 } catch (Exception e) {
974 Log.e(TAG, "Final exception guard in restore:");
975 e.printStackTrace();
976 }
Christopher Tatedf01dea2009-06-09 20:45:02 -0700977 }
978 }
979
980
Christopher Tate487529a2009-04-29 14:03:25 -0700981 // ----- IBackupManager binder interface -----
Christopher Tatedf01dea2009-06-09 20:45:02 -0700982
Christopher Tatea8bf8152009-04-30 11:36:21 -0700983 public void dataChanged(String packageName) throws RemoteException {
Christopher Tate487529a2009-04-29 14:03:25 -0700984 // Record that we need a backup pass for the caller. Since multiple callers
985 // may share a uid, we need to note all candidates within that uid and schedule
986 // a backup pass for each of them.
Joe Onoratob1a7ffe2009-05-06 18:06:21 -0700987
988 Log.d(TAG, "dataChanged packageName=" + packageName);
Christopher Tate63d27002009-06-16 17:16:42 -0700989
990 // If the caller does not hold the BACKUP permission, it can only request a
991 // backup of its own data.
992 HashSet<ApplicationInfo> targets;
993 if ((mContext.checkPermission("android.permission.BACKUP", Binder.getCallingPid(),
994 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
995 targets = mBackupParticipants.get(Binder.getCallingUid());
996 } else {
997 // a caller with full permission can ask to back up any participating app
998 // !!! TODO: allow backup of ANY app?
999 if (DEBUG) Log.v(TAG, "Privileged caller, allowing backup of other apps");
1000 targets = new HashSet<ApplicationInfo>();
1001 int N = mBackupParticipants.size();
1002 for (int i = 0; i < N; i++) {
1003 HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
1004 if (s != null) {
1005 targets.addAll(s);
1006 }
1007 }
1008 }
Christopher Tate487529a2009-04-29 14:03:25 -07001009 if (targets != null) {
1010 synchronized (mQueueLock) {
1011 // Note that this client has made data changes that need to be backed up
Christopher Tate181fafa2009-05-14 11:12:14 -07001012 for (ApplicationInfo app : targets) {
Christopher Tatea8bf8152009-04-30 11:36:21 -07001013 // validate the caller-supplied package name against the known set of
1014 // packages associated with this uid
Christopher Tate181fafa2009-05-14 11:12:14 -07001015 if (app.packageName.equals(packageName)) {
Joe Onorato8ad02812009-05-13 01:41:44 -04001016 // Add the caller to the set of pending backups. If there is
1017 // one already there, then overwrite it, but no harm done.
Christopher Tate181fafa2009-05-14 11:12:14 -07001018 BackupRequest req = new BackupRequest(app, false);
1019 mPendingBackups.put(app, req);
Christopher Tatecde87f42009-06-12 12:55:53 -07001020
1021 // Journal this request in case of crash
1022 writeToJournalLocked(packageName);
Christopher Tate487529a2009-04-29 14:03:25 -07001023 }
1024 }
1025
Christopher Tate181fafa2009-05-14 11:12:14 -07001026 if (DEBUG) {
1027 int numKeys = mPendingBackups.size();
1028 Log.d(TAG, "Scheduling backup for " + numKeys + " participants:");
1029 for (BackupRequest b : mPendingBackups.values()) {
1030 Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
1031 }
1032 }
Christopher Tate487529a2009-04-29 14:03:25 -07001033 // Schedule a backup pass in a few minutes. As backup-eligible data
1034 // keeps changing, continue to defer the backup pass until things
1035 // settle down, to avoid extra overhead.
Christopher Tate6785dd82009-06-18 15:58:25 -07001036 scheduleBackupPassLocked(COLLECTION_INTERVAL);
Christopher Tate487529a2009-04-29 14:03:25 -07001037 }
Christopher Tatedf01dea2009-06-09 20:45:02 -07001038 } else {
1039 Log.w(TAG, "dataChanged but no participant pkg " + packageName);
Christopher Tate487529a2009-04-29 14:03:25 -07001040 }
1041 }
Christopher Tate46758122009-05-06 11:22:00 -07001042
Christopher Tatecde87f42009-06-12 12:55:53 -07001043 private void writeToJournalLocked(String str) {
1044 if (mJournalStream != null) {
1045 try {
1046 mJournalStream.writeUTF(str);
1047 } catch (IOException e) {
1048 Log.e(TAG, "Error writing to backup journal");
1049 mJournalStream = null;
1050 mJournal = null;
1051 }
1052 }
1053 }
1054
Christopher Tateace7f092009-06-15 18:07:25 -07001055 // Run a backup pass immediately for any applications that have declared
1056 // that they have pending updates.
1057 public void backupNow() throws RemoteException {
1058 mContext.enforceCallingPermission("android.permission.BACKUP", "tryBackupNow");
Christopher Tate043dadc2009-06-02 16:11:00 -07001059
Christopher Tateace7f092009-06-15 18:07:25 -07001060 if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
Christopher Tate46758122009-05-06 11:22:00 -07001061 synchronized (mQueueLock) {
Christopher Tate6785dd82009-06-18 15:58:25 -07001062 scheduleBackupPassLocked(0);
Christopher Tate46758122009-05-06 11:22:00 -07001063 }
1064 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07001065
Christopher Tateace7f092009-06-15 18:07:25 -07001066 // Report the currently active transport
1067 public int getCurrentTransport() {
1068 mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
Christopher Tateabce4e82009-06-18 18:35:32 -07001069 Log.v(TAG, "getCurrentTransport() returning " + mTransportId);
Christopher Tateace7f092009-06-15 18:07:25 -07001070 return mTransportId;
1071 }
1072
Christopher Tate043dadc2009-06-02 16:11:00 -07001073 // Select which transport to use for the next backup operation
1074 public int selectBackupTransport(int transportId) {
1075 mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
1076
Dan Egnor87a02bc2009-06-17 02:30:10 -07001077 int prevTransport = mTransportId;
1078 mTransportId = transportId;
Christopher Tateabce4e82009-06-18 18:35:32 -07001079 Log.v(TAG, "selectBackupTransport() set " + mTransportId + " returning " + prevTransport);
Christopher Tate043dadc2009-06-02 16:11:00 -07001080 return prevTransport;
1081 }
1082
1083 // Callback: a requested backup agent has been instantiated. This should only
1084 // be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07001085 public void agentConnected(String packageName, IBinder agentBinder) {
Christopher Tate043dadc2009-06-02 16:11:00 -07001086 synchronized(mAgentConnectLock) {
1087 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
1088 Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
1089 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
1090 mConnectedAgent = agent;
1091 mConnecting = false;
1092 } else {
1093 Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
1094 + " claiming agent connected");
1095 }
1096 mAgentConnectLock.notifyAll();
1097 }
Christopher Tate181fafa2009-05-14 11:12:14 -07001098 }
1099
1100 // Callback: a backup agent has failed to come up, or has unexpectedly quit.
1101 // If the agent failed to come up in the first place, the agentBinder argument
Christopher Tate043dadc2009-06-02 16:11:00 -07001102 // will be null. This should only be called from the Activity Manager.
Christopher Tate181fafa2009-05-14 11:12:14 -07001103 public void agentDisconnected(String packageName) {
1104 // TODO: handle backup being interrupted
Christopher Tate043dadc2009-06-02 16:11:00 -07001105 synchronized(mAgentConnectLock) {
1106 if (Binder.getCallingUid() == Process.SYSTEM_UID) {
1107 mConnectedAgent = null;
1108 mConnecting = false;
1109 } else {
1110 Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
1111 + " claiming agent disconnected");
1112 }
1113 mAgentConnectLock.notifyAll();
1114 }
Christopher Tate181fafa2009-05-14 11:12:14 -07001115 }
Christopher Tate181fafa2009-05-14 11:12:14 -07001116
Christopher Tate8c850b72009-06-07 19:33:20 -07001117 // Hand off a restore session
1118 public IRestoreSession beginRestoreSession(int transportID) {
1119 mContext.enforceCallingPermission("android.permission.BACKUP", "beginRestoreSession");
Christopher Tatef68eb502009-06-16 11:02:01 -07001120
1121 synchronized(this) {
1122 if (mActiveRestoreSession != null) {
1123 Log.d(TAG, "Restore session requested but one already active");
1124 return null;
1125 }
1126 mActiveRestoreSession = new RestoreSession(transportID);
1127 }
1128 return mActiveRestoreSession;
Christopher Tate8c850b72009-06-07 19:33:20 -07001129 }
Christopher Tate043dadc2009-06-02 16:11:00 -07001130
Christopher Tate9b3905c2009-06-08 15:24:01 -07001131 // ----- Restore session -----
1132
1133 class RestoreSession extends IRestoreSession.Stub {
Christopher Tatef68eb502009-06-16 11:02:01 -07001134 private static final String TAG = "RestoreSession";
1135
Christopher Tate9b3905c2009-06-08 15:24:01 -07001136 private IBackupTransport mRestoreTransport = null;
1137 RestoreSet[] mRestoreSets = null;
1138
1139 RestoreSession(int transportID) {
Dan Egnor87a02bc2009-06-17 02:30:10 -07001140 mRestoreTransport = getTransport(transportID);
Christopher Tate9b3905c2009-06-08 15:24:01 -07001141 }
1142
1143 // --- Binder interface ---
1144 public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
Christopher Tate9bbc21a2009-06-10 20:23:25 -07001145 mContext.enforceCallingPermission("android.permission.BACKUP",
1146 "getAvailableRestoreSets");
1147
Christopher Tatef68eb502009-06-16 11:02:01 -07001148 try {
Christopher Tate9b3905c2009-06-08 15:24:01 -07001149 synchronized(this) {
1150 if (mRestoreSets == null) {
1151 mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
1152 }
1153 return mRestoreSets;
1154 }
Christopher Tatef68eb502009-06-16 11:02:01 -07001155 } catch (RuntimeException e) {
1156 Log.d(TAG, "getAvailableRestoreSets exception");
1157 e.printStackTrace();
1158 throw e;
1159 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07001160 }
1161
1162 public int performRestore(int token) throws android.os.RemoteException {
Christopher Tate9bbc21a2009-06-10 20:23:25 -07001163 mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
1164
1165 if (mRestoreSets != null) {
1166 for (int i = 0; i < mRestoreSets.length; i++) {
1167 if (token == mRestoreSets[i].token) {
1168 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE,
1169 mRestoreTransport);
1170 msg.arg1 = token;
1171 mBackupHandler.sendMessage(msg);
1172 return 0;
1173 }
1174 }
Christopher Tatef68eb502009-06-16 11:02:01 -07001175 } else {
1176 if (DEBUG) Log.v(TAG, "No current restore set, not doing restore");
Christopher Tate9bbc21a2009-06-10 20:23:25 -07001177 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07001178 return -1;
1179 }
1180
1181 public void endRestoreSession() throws android.os.RemoteException {
Christopher Tate9bbc21a2009-06-10 20:23:25 -07001182 mContext.enforceCallingPermission("android.permission.BACKUP",
1183 "endRestoreSession");
1184
Christopher Tate9b3905c2009-06-08 15:24:01 -07001185 mRestoreTransport.endSession();
1186 mRestoreTransport = null;
Christopher Tatef68eb502009-06-16 11:02:01 -07001187 synchronized(BackupManagerService.this) {
1188 if (BackupManagerService.this.mActiveRestoreSession == this) {
1189 BackupManagerService.this.mActiveRestoreSession = null;
1190 } else {
1191 Log.e(TAG, "ending non-current restore session");
1192 }
1193 }
Christopher Tate9b3905c2009-06-08 15:24:01 -07001194 }
1195 }
1196
Christopher Tate043dadc2009-06-02 16:11:00 -07001197
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07001198 @Override
1199 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1200 synchronized (mQueueLock) {
1201 int N = mBackupParticipants.size();
1202 pw.println("Participants:");
1203 for (int i=0; i<N; i++) {
1204 int uid = mBackupParticipants.keyAt(i);
1205 pw.print(" uid: ");
1206 pw.println(uid);
Christopher Tate181fafa2009-05-14 11:12:14 -07001207 HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
1208 for (ApplicationInfo app: participants) {
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07001209 pw.print(" ");
Christopher Tate181fafa2009-05-14 11:12:14 -07001210 pw.println(app.toString());
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07001211 }
1212 }
Christopher Tate6aa41f42009-06-19 14:14:22 -07001213 pw.println("Pending: " + mPendingBackups.size());
1214 for (BackupRequest req : mPendingBackups.values()) {
1215 pw.print(" ");
1216 pw.println(req);
Christopher Tate181fafa2009-05-14 11:12:14 -07001217 }
Joe Onoratob1a7ffe2009-05-06 18:06:21 -07001218 }
1219 }
Christopher Tate487529a2009-04-29 14:03:25 -07001220}