1 /*
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 
17 package com.android.server.backup;
18 
19 import android.app.ActivityManagerNative;
20 import android.app.AlarmManager;
21 import android.app.AppGlobals;
22 import android.app.IActivityManager;
23 import android.app.IApplicationThread;
24 import android.app.IBackupAgent;
25 import android.app.PackageInstallObserver;
26 import android.app.PendingIntent;
27 import android.app.backup.BackupAgent;
28 import android.app.backup.BackupDataInput;
29 import android.app.backup.BackupDataOutput;
30 import android.app.backup.BackupTransport;
31 import android.app.backup.FullBackup;
32 import android.app.backup.FullBackupDataOutput;
33 import android.app.backup.RestoreDescription;
34 import android.app.backup.RestoreSet;
35 import android.app.backup.IBackupManager;
36 import android.app.backup.IFullBackupRestoreObserver;
37 import android.app.backup.IRestoreObserver;
38 import android.app.backup.IRestoreSession;
39 import android.content.ActivityNotFoundException;
40 import android.content.BroadcastReceiver;
41 import android.content.ComponentName;
42 import android.content.ContentResolver;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.content.ServiceConnection;
47 import android.content.pm.ApplicationInfo;
48 import android.content.pm.IPackageDataObserver;
49 import android.content.pm.IPackageDeleteObserver;
50 import android.content.pm.IPackageManager;
51 import android.content.pm.PackageInfo;
52 import android.content.pm.PackageManager;
53 import android.content.pm.ResolveInfo;
54 import android.content.pm.ServiceInfo;
55 import android.content.pm.Signature;
56 import android.content.pm.PackageManager.NameNotFoundException;
57 import android.database.ContentObserver;
58 import android.net.Uri;
59 import android.os.Binder;
60 import android.os.Build;
61 import android.os.Bundle;
62 import android.os.Environment;
63 import android.os.Handler;
64 import android.os.HandlerThread;
65 import android.os.IBinder;
66 import android.os.Looper;
67 import android.os.Message;
68 import android.os.ParcelFileDescriptor;
69 import android.os.PowerManager;
70 import android.os.Process;
71 import android.os.RemoteException;
72 import android.os.SELinux;
73 import android.os.ServiceManager;
74 import android.os.SystemClock;
75 import android.os.UserHandle;
76 import android.os.WorkSource;
77 import android.os.Environment.UserEnvironment;
78 import android.os.storage.IMountService;
79 import android.os.storage.StorageManager;
80 import android.provider.Settings;
81 import android.system.ErrnoException;
82 import android.system.Os;
83 import android.util.ArrayMap;
84 import android.util.AtomicFile;
85 import android.util.EventLog;
86 import android.util.Log;
87 import android.util.Slog;
88 import android.util.SparseArray;
89 import android.util.StringBuilderPrinter;
90 
91 import com.android.internal.annotations.GuardedBy;
92 import com.android.internal.backup.IBackupTransport;
93 import com.android.internal.backup.IObbBackupService;
94 import com.android.server.AppWidgetBackupBridge;
95 import com.android.server.EventLogTags;
96 import com.android.server.SystemService;
97 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
98 
99 import java.io.BufferedInputStream;
100 import java.io.BufferedOutputStream;
101 import java.io.ByteArrayInputStream;
102 import java.io.ByteArrayOutputStream;
103 import java.io.DataInputStream;
104 import java.io.DataOutputStream;
105 import java.io.EOFException;
106 import java.io.File;
107 import java.io.FileDescriptor;
108 import java.io.FileInputStream;
109 import java.io.FileNotFoundException;
110 import java.io.FileOutputStream;
111 import java.io.IOException;
112 import java.io.InputStream;
113 import java.io.OutputStream;
114 import java.io.PrintWriter;
115 import java.io.RandomAccessFile;
116 import java.security.InvalidAlgorithmParameterException;
117 import java.security.InvalidKeyException;
118 import java.security.Key;
119 import java.security.MessageDigest;
120 import java.security.NoSuchAlgorithmException;
121 import java.security.SecureRandom;
122 import java.security.spec.InvalidKeySpecException;
123 import java.security.spec.KeySpec;
124 import java.text.SimpleDateFormat;
125 import java.util.ArrayList;
126 import java.util.Arrays;
127 import java.util.Collections;
128 import java.util.Date;
129 import java.util.HashMap;
130 import java.util.HashSet;
131 import java.util.Iterator;
132 import java.util.List;
133 import java.util.Map;
134 import java.util.Map.Entry;
135 import java.util.Objects;
136 import java.util.Random;
137 import java.util.Set;
138 import java.util.TreeMap;
139 import java.util.concurrent.CountDownLatch;
140 import java.util.concurrent.atomic.AtomicBoolean;
141 import java.util.concurrent.atomic.AtomicInteger;
142 import java.util.zip.Deflater;
143 import java.util.zip.DeflaterOutputStream;
144 import java.util.zip.InflaterInputStream;
145 
146 import javax.crypto.BadPaddingException;
147 import javax.crypto.Cipher;
148 import javax.crypto.CipherInputStream;
149 import javax.crypto.CipherOutputStream;
150 import javax.crypto.IllegalBlockSizeException;
151 import javax.crypto.NoSuchPaddingException;
152 import javax.crypto.SecretKey;
153 import javax.crypto.SecretKeyFactory;
154 import javax.crypto.spec.IvParameterSpec;
155 import javax.crypto.spec.PBEKeySpec;
156 import javax.crypto.spec.SecretKeySpec;
157 
158 import libcore.io.IoUtils;
159 
160 public class BackupManagerService {
161 
162     private static final String TAG = "BackupManagerService";
163     static final boolean DEBUG = true;
164     static final boolean MORE_DEBUG = false;
165     static final boolean DEBUG_SCHEDULING = MORE_DEBUG || true;
166 
167     // System-private key used for backing up an app's widget state.  Must
168     // begin with U+FFxx by convention (we reserve all keys starting
169     // with U+FF00 or higher for system use).
170     static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
171 
172     // Historical and current algorithm names
173     static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
174     static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
175 
176     // Name and current contents version of the full-backup manifest file
177     //
178     // Manifest version history:
179     //
180     // 1 : initial release
181     static final String BACKUP_MANIFEST_FILENAME = "_manifest";
182     static final int BACKUP_MANIFEST_VERSION = 1;
183 
184     // External archive format version history:
185     //
186     // 1 : initial release
187     // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
188     // 3 : introduced "_meta" metadata file; no other format change per se
189     static final int BACKUP_FILE_VERSION = 3;
190     static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
191     static final int BACKUP_PW_FILE_VERSION = 2;
192     static final String BACKUP_METADATA_FILENAME = "_meta";
193     static final int BACKUP_METADATA_VERSION = 1;
194     static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
195     static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
196 
197     static final String SETTINGS_PACKAGE = "com.android.providers.settings";
198     static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
199     static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
200 
201     // Retry interval for clear/init when the transport is unavailable
202     private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
203 
204     private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
205     private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
206     private static final int MSG_RUN_BACKUP = 1;
207     private static final int MSG_RUN_ADB_BACKUP = 2;
208     private static final int MSG_RUN_RESTORE = 3;
209     private static final int MSG_RUN_CLEAR = 4;
210     private static final int MSG_RUN_INITIALIZE = 5;
211     private static final int MSG_RUN_GET_RESTORE_SETS = 6;
212     private static final int MSG_TIMEOUT = 7;
213     private static final int MSG_RESTORE_TIMEOUT = 8;
214     private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
215     private static final int MSG_RUN_ADB_RESTORE = 10;
216     private static final int MSG_RETRY_INIT = 11;
217     private static final int MSG_RETRY_CLEAR = 12;
218     private static final int MSG_WIDGET_BROADCAST = 13;
219     private static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14;
220 
221     // backup task state machine tick
222     static final int MSG_BACKUP_RESTORE_STEP = 20;
223     static final int MSG_OP_COMPLETE = 21;
224 
225     // Timeout interval for deciding that a bind or clear-data has taken too long
226     static final long TIMEOUT_INTERVAL = 10 * 1000;
227 
228     // Timeout intervals for agent backup & restore operations
229     static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
230     static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
231     static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
232     static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
233     static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
234 
235     // User confirmation timeout for a full backup/restore operation.  It's this long in
236     // order to give them time to enter the backup password.
237     static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
238 
239     // How long between attempts to perform a full-data backup of any given app
240     static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
241 
242     Context mContext;
243     private PackageManager mPackageManager;
244     IPackageManager mPackageManagerBinder;
245     private IActivityManager mActivityManager;
246     private PowerManager mPowerManager;
247     private AlarmManager mAlarmManager;
248     private IMountService mMountService;
249     IBackupManager mBackupManagerBinder;
250 
251     boolean mEnabled;   // access to this is synchronized on 'this'
252     boolean mProvisioned;
253     boolean mAutoRestore;
254     PowerManager.WakeLock mWakelock;
255     HandlerThread mHandlerThread;
256     BackupHandler mBackupHandler;
257     PendingIntent mRunBackupIntent, mRunInitIntent;
258     BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
259     // map UIDs to the set of participating packages under that UID
260     final SparseArray<HashSet<String>> mBackupParticipants
261             = new SparseArray<HashSet<String>>();
262     // set of backup services that have pending changes
263     class BackupRequest {
264         public String packageName;
265 
BackupRequest(String pkgName)266         BackupRequest(String pkgName) {
267             packageName = pkgName;
268         }
269 
toString()270         public String toString() {
271             return "BackupRequest{pkg=" + packageName + "}";
272         }
273     }
274     // Backups that we haven't started yet.  Keys are package names.
275     HashMap<String,BackupRequest> mPendingBackups
276             = new HashMap<String,BackupRequest>();
277 
278     // Pseudoname that we use for the Package Manager metadata "package"
279     static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
280 
281     // locking around the pending-backup management
282     final Object mQueueLock = new Object();
283 
284     // The thread performing the sequence of queued backups binds to each app's agent
285     // in succession.  Bind notifications are asynchronously delivered through the
286     // Activity Manager; use this lock object to signal when a requested binding has
287     // completed.
288     final Object mAgentConnectLock = new Object();
289     IBackupAgent mConnectedAgent;
290     volatile boolean mBackupRunning;
291     volatile boolean mConnecting;
292     volatile long mLastBackupPass;
293 
294     // For debugging, we maintain a progress trace of operations during backup
295     static final boolean DEBUG_BACKUP_TRACE = true;
296     final List<String> mBackupTrace = new ArrayList<String>();
297 
298     // A similar synchronization mechanism around clearing apps' data for restore
299     final Object mClearDataLock = new Object();
300     volatile boolean mClearingData;
301 
302     // Transport bookkeeping
303     final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
304     final ArrayMap<String,String> mTransportNames
305             = new ArrayMap<String,String>();             // component name -> registration name
306     final ArrayMap<String,IBackupTransport> mTransports
307             = new ArrayMap<String,IBackupTransport>();   // registration name -> binder
308     final ArrayMap<String,TransportConnection> mTransportConnections
309             = new ArrayMap<String,TransportConnection>();
310     String mCurrentTransport;
311     ActiveRestoreSession mActiveRestoreSession;
312 
313     // Watch the device provisioning operation during setup
314     ContentObserver mProvisionedObserver;
315 
316     // The published binder is actually to a singleton trampoline object that calls
317     // through to the proper code.  This indirection lets us turn down the heavy
318     // implementation object on the fly without disturbing binders that have been
319     // cached elsewhere in the system.
320     static Trampoline sInstance;
getInstance()321     static Trampoline getInstance() {
322         // Always constructed during system bringup, so no need to lazy-init
323         return sInstance;
324     }
325 
326     public static final class Lifecycle extends SystemService {
327 
Lifecycle(Context context)328         public Lifecycle(Context context) {
329             super(context);
330             sInstance = new Trampoline(context);
331         }
332 
333         @Override
onStart()334         public void onStart() {
335             publishBinderService(Context.BACKUP_SERVICE, sInstance);
336         }
337 
338         @Override
onBootPhase(int phase)339         public void onBootPhase(int phase) {
340             if (phase == PHASE_SYSTEM_SERVICES_READY) {
341                 sInstance.initialize(UserHandle.USER_OWNER);
342             } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
343                 ContentResolver r = sInstance.mContext.getContentResolver();
344                 boolean areEnabled = Settings.Secure.getInt(r,
345                         Settings.Secure.BACKUP_ENABLED, 0) != 0;
346                 try {
347                     sInstance.setBackupEnabled(areEnabled);
348                 } catch (RemoteException e) {
349                     // can't happen; it's a local object
350                 }
351             }
352         }
353     }
354 
355     class ProvisionedObserver extends ContentObserver {
ProvisionedObserver(Handler handler)356         public ProvisionedObserver(Handler handler) {
357             super(handler);
358         }
359 
onChange(boolean selfChange)360         public void onChange(boolean selfChange) {
361             final boolean wasProvisioned = mProvisioned;
362             final boolean isProvisioned = deviceIsProvisioned();
363             // latch: never unprovision
364             mProvisioned = wasProvisioned || isProvisioned;
365             if (MORE_DEBUG) {
366                 Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
367                         + " is=" + isProvisioned + " now=" + mProvisioned);
368             }
369 
370             synchronized (mQueueLock) {
371                 if (mProvisioned && !wasProvisioned && mEnabled) {
372                     // we're now good to go, so start the backup alarms
373                     if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
374                     KeyValueBackupJob.schedule(mContext);
375                     scheduleNextFullBackupJob(0);
376                 }
377             }
378         }
379     }
380 
381     class RestoreGetSetsParams {
382         public IBackupTransport transport;
383         public ActiveRestoreSession session;
384         public IRestoreObserver observer;
385 
RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session, IRestoreObserver _observer)386         RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
387                 IRestoreObserver _observer) {
388             transport = _transport;
389             session = _session;
390             observer = _observer;
391         }
392     }
393 
394     class RestoreParams {
395         public IBackupTransport transport;
396         public String dirName;
397         public IRestoreObserver observer;
398         public long token;
399         public PackageInfo pkgInfo;
400         public int pmToken; // in post-install restore, the PM's token for this transaction
401         public boolean isSystemRestore;
402         public String[] filterSet;
403 
404         /**
405          * Restore a single package; no kill after restore
406          */
RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token, PackageInfo _pkg)407         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
408                 long _token, PackageInfo _pkg) {
409             transport = _transport;
410             dirName = _dirName;
411             observer = _obs;
412             token = _token;
413             pkgInfo = _pkg;
414             pmToken = 0;
415             isSystemRestore = false;
416             filterSet = null;
417         }
418 
419         /**
420          * Restore at install: PM token needed, kill after restore
421          */
RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token, String _pkgName, int _pmToken)422         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
423                 long _token, String _pkgName, int _pmToken) {
424             transport = _transport;
425             dirName = _dirName;
426             observer = _obs;
427             token = _token;
428             pkgInfo = null;
429             pmToken = _pmToken;
430             isSystemRestore = false;
431             filterSet = new String[] { _pkgName };
432         }
433 
434         /**
435          * Restore everything possible.  This is the form that Setup Wizard or similar
436          * restore UXes use.
437          */
RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token)438         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
439                 long _token) {
440             transport = _transport;
441             dirName = _dirName;
442             observer = _obs;
443             token = _token;
444             pkgInfo = null;
445             pmToken = 0;
446             isSystemRestore = true;
447             filterSet = null;
448         }
449 
450         /**
451          * Restore some set of packages.  Leave this one up to the caller to specify
452          * whether it's to be considered a system-level restore.
453          */
RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token, String[] _filterSet, boolean _isSystemRestore)454         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
455                 long _token, String[] _filterSet, boolean _isSystemRestore) {
456             transport = _transport;
457             dirName = _dirName;
458             observer = _obs;
459             token = _token;
460             pkgInfo = null;
461             pmToken = 0;
462             isSystemRestore = _isSystemRestore;
463             filterSet = _filterSet;
464         }
465     }
466 
467     class ClearParams {
468         public IBackupTransport transport;
469         public PackageInfo packageInfo;
470 
ClearParams(IBackupTransport _transport, PackageInfo _info)471         ClearParams(IBackupTransport _transport, PackageInfo _info) {
472             transport = _transport;
473             packageInfo = _info;
474         }
475     }
476 
477     class ClearRetryParams {
478         public String transportName;
479         public String packageName;
480 
ClearRetryParams(String transport, String pkg)481         ClearRetryParams(String transport, String pkg) {
482             transportName = transport;
483             packageName = pkg;
484         }
485     }
486 
487     class FullParams {
488         public ParcelFileDescriptor fd;
489         public final AtomicBoolean latch;
490         public IFullBackupRestoreObserver observer;
491         public String curPassword;     // filled in by the confirmation step
492         public String encryptPassword;
493 
FullParams()494         FullParams() {
495             latch = new AtomicBoolean(false);
496         }
497     }
498 
499     class FullBackupParams extends FullParams {
500         public boolean includeApks;
501         public boolean includeObbs;
502         public boolean includeShared;
503         public boolean doWidgets;
504         public boolean allApps;
505         public boolean includeSystem;
506         public boolean doCompress;
507         public String[] packages;
508 
FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs, boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem, boolean compress, String[] pkgList)509         FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
510                 boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
511                 boolean compress, String[] pkgList) {
512             fd = output;
513             includeApks = saveApks;
514             includeObbs = saveObbs;
515             includeShared = saveShared;
516             doWidgets = alsoWidgets;
517             allApps = doAllApps;
518             includeSystem = doSystem;
519             doCompress = compress;
520             packages = pkgList;
521         }
522     }
523 
524     class FullRestoreParams extends FullParams {
FullRestoreParams(ParcelFileDescriptor input)525         FullRestoreParams(ParcelFileDescriptor input) {
526             fd = input;
527         }
528     }
529 
530     // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
531     // token is the index of the entry in the pending-operations list.
532     static final int OP_PENDING = 0;
533     static final int OP_ACKNOWLEDGED = 1;
534     static final int OP_TIMEOUT = -1;
535 
536     class Operation {
537         public int state;
538         public BackupRestoreTask callback;
539 
Operation(int initialState, BackupRestoreTask callbackObj)540         Operation(int initialState, BackupRestoreTask callbackObj) {
541             state = initialState;
542             callback = callbackObj;
543         }
544     }
545     final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
546     final Object mCurrentOpLock = new Object();
547     final Random mTokenGenerator = new Random();
548 
549     final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
550 
551     // Where we keep our journal files and other bookkeeping
552     File mBaseStateDir;
553     File mDataDir;
554     File mJournalDir;
555     File mJournal;
556 
557     // Backup password, if any, and the file where it's saved.  What is stored is not the
558     // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but
559     // persisted) salt.  Validation is performed by running the challenge text through the
560     // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches
561     // the saved hash string, then the challenge text matches the originally supplied
562     // password text.
563     private final SecureRandom mRng = new SecureRandom();
564     private String mPasswordHash;
565     private File mPasswordHashFile;
566     private int mPasswordVersion;
567     private File mPasswordVersionFile;
568     private byte[] mPasswordSalt;
569 
570     // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys
571     static final int PBKDF2_HASH_ROUNDS = 10000;
572     static final int PBKDF2_KEY_SIZE = 256;     // bits
573     static final int PBKDF2_SALT_SIZE = 512;    // bits
574     static final String ENCRYPTION_ALGORITHM_NAME = "AES-256";
575 
576     // Keep a log of all the apps we've ever backed up, and what the
577     // dataset tokens are for both the current backup dataset and
578     // the ancestral dataset.
579     private File mEverStored;
580     HashSet<String> mEverStoredApps = new HashSet<String>();
581 
582     static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;  // increment when the schema changes
583     File mTokenFile;
584     Set<String> mAncestralPackages = null;
585     long mAncestralToken = 0;
586     long mCurrentToken = 0;
587 
588     // Persistently track the need to do a full init
589     static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
590     HashSet<String> mPendingInits = new HashSet<String>();  // transport names
591 
592     // Round-robin queue for scheduling full backup passes
593     static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
594     class FullBackupEntry implements Comparable<FullBackupEntry> {
595         String packageName;
596         long lastBackup;
597 
FullBackupEntry(String pkg, long when)598         FullBackupEntry(String pkg, long when) {
599             packageName = pkg;
600             lastBackup = when;
601         }
602 
603         @Override
compareTo(FullBackupEntry other)604         public int compareTo(FullBackupEntry other) {
605             if (lastBackup < other.lastBackup) return -1;
606             else if (lastBackup > other.lastBackup) return 1;
607             else return 0;
608         }
609     }
610 
611     File mFullBackupScheduleFile;
612     // If we're running a schedule-driven full backup, this is the task instance doing it
613 
614     @GuardedBy("mQueueLock")
615     PerformFullTransportBackupTask mRunningFullBackupTask;
616 
617     @GuardedBy("mQueueLock")
618     ArrayList<FullBackupEntry> mFullBackupQueue;
619 
620     // Utility: build a new random integer token
generateToken()621     int generateToken() {
622         int token;
623         do {
624             synchronized (mTokenGenerator) {
625                 token = mTokenGenerator.nextInt();
626             }
627         } while (token < 0);
628         return token;
629     }
630 
631     // High level policy: apps are generally ineligible for backup if certain conditions apply
appIsEligibleForBackup(ApplicationInfo app)632     public static boolean appIsEligibleForBackup(ApplicationInfo app) {
633         // 1. their manifest states android:allowBackup="false"
634         if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
635             return false;
636         }
637 
638         // 2. they run as a system-level uid but do not supply their own backup agent
639         if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) {
640             return false;
641         }
642 
643         // 3. it is the special shared-storage backup package used for 'adb backup'
644         if (app.packageName.equals(BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE)) {
645             return false;
646         }
647 
648         return true;
649     }
650 
651     /* does *not* check overall backup eligibility policy! */
appGetsFullBackup(PackageInfo pkg)652     public static boolean appGetsFullBackup(PackageInfo pkg) {
653         if (pkg.applicationInfo.backupAgentName != null) {
654             // If it has an agent, it gets full backups only if it says so
655             return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0;
656         }
657 
658         // No agent or fullBackupOnly="true" means we do indeed perform full-data backups for it
659         return true;
660     }
661 
662     // ----- Asynchronous backup/restore handler thread -----
663 
664     private class BackupHandler extends Handler {
BackupHandler(Looper looper)665         public BackupHandler(Looper looper) {
666             super(looper);
667         }
668 
handleMessage(Message msg)669         public void handleMessage(Message msg) {
670 
671             switch (msg.what) {
672             case MSG_RUN_BACKUP:
673             {
674                 mLastBackupPass = System.currentTimeMillis();
675 
676                 IBackupTransport transport = getTransport(mCurrentTransport);
677                 if (transport == null) {
678                     Slog.v(TAG, "Backup requested but no transport available");
679                     synchronized (mQueueLock) {
680                         mBackupRunning = false;
681                     }
682                     mWakelock.release();
683                     break;
684                 }
685 
686                 // snapshot the pending-backup set and work on that
687                 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
688                 File oldJournal = mJournal;
689                 synchronized (mQueueLock) {
690                     // Do we have any work to do?  Construct the work queue
691                     // then release the synchronization lock to actually run
692                     // the backup.
693                     if (mPendingBackups.size() > 0) {
694                         for (BackupRequest b: mPendingBackups.values()) {
695                             queue.add(b);
696                         }
697                         if (DEBUG) Slog.v(TAG, "clearing pending backups");
698                         mPendingBackups.clear();
699 
700                         // Start a new backup-queue journal file too
701                         mJournal = null;
702 
703                     }
704                 }
705 
706                 // At this point, we have started a new journal file, and the old
707                 // file identity is being passed to the backup processing task.
708                 // When it completes successfully, that old journal file will be
709                 // deleted.  If we crash prior to that, the old journal is parsed
710                 // at next boot and the journaled requests fulfilled.
711                 boolean staged = true;
712                 if (queue.size() > 0) {
713                     // Spin up a backup state sequence and set it running
714                     try {
715                         String dirName = transport.transportDirName();
716                         PerformBackupTask pbt = new PerformBackupTask(transport, dirName,
717                                 queue, oldJournal);
718                         Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
719                         sendMessage(pbtMessage);
720                     } catch (RemoteException e) {
721                         // unable to ask the transport its dir name -- transient failure, since
722                         // the above check succeeded.  Try again next time.
723                         Slog.e(TAG, "Transport became unavailable attempting backup");
724                         staged = false;
725                     }
726                 } else {
727                     Slog.v(TAG, "Backup requested but nothing pending");
728                     staged = false;
729                 }
730 
731                 if (!staged) {
732                     // if we didn't actually hand off the wakelock, rewind until next time
733                     synchronized (mQueueLock) {
734                         mBackupRunning = false;
735                     }
736                     mWakelock.release();
737                 }
738                 break;
739             }
740 
741             case MSG_BACKUP_RESTORE_STEP:
742             {
743                 try {
744                     BackupRestoreTask task = (BackupRestoreTask) msg.obj;
745                     if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing");
746                     task.execute();
747                 } catch (ClassCastException e) {
748                     Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
749                 }
750                 break;
751             }
752 
753             case MSG_OP_COMPLETE:
754             {
755                 try {
756                     BackupRestoreTask task = (BackupRestoreTask) msg.obj;
757                     task.operationComplete(msg.arg1);
758                 } catch (ClassCastException e) {
759                     Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
760                 }
761                 break;
762             }
763 
764             case MSG_RUN_ADB_BACKUP:
765             {
766                 // TODO: refactor full backup to be a looper-based state machine
767                 // similar to normal backup/restore.
768                 FullBackupParams params = (FullBackupParams)msg.obj;
769                 PerformAdbBackupTask task = new PerformAdbBackupTask(params.fd,
770                         params.observer, params.includeApks, params.includeObbs,
771                         params.includeShared, params.doWidgets,
772                         params.curPassword, params.encryptPassword,
773                         params.allApps, params.includeSystem, params.doCompress,
774                         params.packages, params.latch);
775                 (new Thread(task, "adb-backup")).start();
776                 break;
777             }
778 
779             case MSG_RUN_FULL_TRANSPORT_BACKUP:
780             {
781                 PerformFullTransportBackupTask task = (PerformFullTransportBackupTask) msg.obj;
782                 (new Thread(task, "transport-backup")).start();
783                 break;
784             }
785 
786             case MSG_RUN_RESTORE:
787             {
788                 RestoreParams params = (RestoreParams)msg.obj;
789                 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
790                 BackupRestoreTask task = new PerformUnifiedRestoreTask(params.transport,
791                         params.observer, params.token, params.pkgInfo, params.pmToken,
792                         params.isSystemRestore, params.filterSet);
793                 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
794                 sendMessage(restoreMsg);
795                 break;
796             }
797 
798             case MSG_RUN_ADB_RESTORE:
799             {
800                 // TODO: refactor full restore to be a looper-based state machine
801                 // similar to normal backup/restore.
802                 FullRestoreParams params = (FullRestoreParams)msg.obj;
803                 PerformAdbRestoreTask task = new PerformAdbRestoreTask(params.fd,
804                         params.curPassword, params.encryptPassword,
805                         params.observer, params.latch);
806                 (new Thread(task, "adb-restore")).start();
807                 break;
808             }
809 
810             case MSG_RUN_CLEAR:
811             {
812                 ClearParams params = (ClearParams)msg.obj;
813                 (new PerformClearTask(params.transport, params.packageInfo)).run();
814                 break;
815             }
816 
817             case MSG_RETRY_CLEAR:
818             {
819                 // reenqueues if the transport remains unavailable
820                 ClearRetryParams params = (ClearRetryParams)msg.obj;
821                 clearBackupData(params.transportName, params.packageName);
822                 break;
823             }
824 
825             case MSG_RUN_INITIALIZE:
826             {
827                 HashSet<String> queue;
828 
829                 // Snapshot the pending-init queue and work on that
830                 synchronized (mQueueLock) {
831                     queue = new HashSet<String>(mPendingInits);
832                     mPendingInits.clear();
833                 }
834 
835                 (new PerformInitializeTask(queue)).run();
836                 break;
837             }
838 
839             case MSG_RETRY_INIT:
840             {
841                 synchronized (mQueueLock) {
842                     recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj);
843                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
844                             mRunInitIntent);
845                 }
846                 break;
847             }
848 
849             case MSG_RUN_GET_RESTORE_SETS:
850             {
851                 // Like other async operations, this is entered with the wakelock held
852                 RestoreSet[] sets = null;
853                 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
854                 try {
855                     sets = params.transport.getAvailableRestoreSets();
856                     // cache the result in the active session
857                     synchronized (params.session) {
858                         params.session.mRestoreSets = sets;
859                     }
860                     if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
861                 } catch (Exception e) {
862                     Slog.e(TAG, "Error from transport getting set list");
863                 } finally {
864                     if (params.observer != null) {
865                         try {
866                             params.observer.restoreSetsAvailable(sets);
867                         } catch (RemoteException re) {
868                             Slog.e(TAG, "Unable to report listing to observer");
869                         } catch (Exception e) {
870                             Slog.e(TAG, "Restore observer threw", e);
871                         }
872                     }
873 
874                     // Done: reset the session timeout clock
875                     removeMessages(MSG_RESTORE_TIMEOUT);
876                     sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
877 
878                     mWakelock.release();
879                 }
880                 break;
881             }
882 
883             case MSG_TIMEOUT:
884             {
885                 handleTimeout(msg.arg1, msg.obj);
886                 break;
887             }
888 
889             case MSG_RESTORE_TIMEOUT:
890             {
891                 synchronized (BackupManagerService.this) {
892                     if (mActiveRestoreSession != null) {
893                         // Client app left the restore session dangling.  We know that it
894                         // can't be in the middle of an actual restore operation because
895                         // the timeout is suspended while a restore is in progress.  Clean
896                         // up now.
897                         Slog.w(TAG, "Restore session timed out; aborting");
898                         mActiveRestoreSession.markTimedOut();
899                         post(mActiveRestoreSession.new EndRestoreRunnable(
900                                 BackupManagerService.this, mActiveRestoreSession));
901                     }
902                 }
903                 break;
904             }
905 
906             case MSG_FULL_CONFIRMATION_TIMEOUT:
907             {
908                 synchronized (mFullConfirmations) {
909                     FullParams params = mFullConfirmations.get(msg.arg1);
910                     if (params != null) {
911                         Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
912 
913                         // Release the waiter; timeout == completion
914                         signalFullBackupRestoreCompletion(params);
915 
916                         // Remove the token from the set
917                         mFullConfirmations.delete(msg.arg1);
918 
919                         // Report a timeout to the observer, if any
920                         if (params.observer != null) {
921                             try {
922                                 params.observer.onTimeout();
923                             } catch (RemoteException e) {
924                                 /* don't care if the app has gone away */
925                             }
926                         }
927                     } else {
928                         Slog.d(TAG, "couldn't find params for token " + msg.arg1);
929                     }
930                 }
931                 break;
932             }
933 
934             case MSG_WIDGET_BROADCAST:
935             {
936                 final Intent intent = (Intent) msg.obj;
937                 mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
938                 break;
939             }
940             }
941         }
942     }
943 
944     // ----- Debug-only backup operation trace -----
addBackupTrace(String s)945     void addBackupTrace(String s) {
946         if (DEBUG_BACKUP_TRACE) {
947             synchronized (mBackupTrace) {
948                 mBackupTrace.add(s);
949             }
950         }
951     }
952 
clearBackupTrace()953     void clearBackupTrace() {
954         if (DEBUG_BACKUP_TRACE) {
955             synchronized (mBackupTrace) {
956                 mBackupTrace.clear();
957             }
958         }
959     }
960 
961     // ----- Main service implementation -----
962 
BackupManagerService(Context context, Trampoline parent)963     public BackupManagerService(Context context, Trampoline parent) {
964         mContext = context;
965         mPackageManager = context.getPackageManager();
966         mPackageManagerBinder = AppGlobals.getPackageManager();
967         mActivityManager = ActivityManagerNative.getDefault();
968 
969         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
970         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
971         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
972 
973         mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
974 
975         // spin up the backup/restore handler thread
976         mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
977         mHandlerThread.start();
978         mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
979 
980         // Set up our bookkeeping
981         final ContentResolver resolver = context.getContentResolver();
982         mProvisioned = Settings.Global.getInt(resolver,
983                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
984         mAutoRestore = Settings.Secure.getInt(resolver,
985                 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
986 
987         mProvisionedObserver = new ProvisionedObserver(mBackupHandler);
988         resolver.registerContentObserver(
989                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
990                 false, mProvisionedObserver);
991 
992         // If Encrypted file systems is enabled or disabled, this call will return the
993         // correct directory.
994         mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
995         mBaseStateDir.mkdirs();
996         if (!SELinux.restorecon(mBaseStateDir)) {
997             Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
998         }
999         mDataDir = Environment.getDownloadCacheDirectory();
1000 
1001         mPasswordVersion = 1;       // unless we hear otherwise
1002         mPasswordVersionFile = new File(mBaseStateDir, "pwversion");
1003         if (mPasswordVersionFile.exists()) {
1004             FileInputStream fin = null;
1005             DataInputStream in = null;
1006             try {
1007                 fin = new FileInputStream(mPasswordVersionFile);
1008                 in = new DataInputStream(fin);
1009                 mPasswordVersion = in.readInt();
1010             } catch (IOException e) {
1011                 Slog.e(TAG, "Unable to read backup pw version");
1012             } finally {
1013                 try {
1014                     if (in != null) in.close();
1015                     if (fin != null) fin.close();
1016                 } catch (IOException e) {
1017                     Slog.w(TAG, "Error closing pw version files");
1018                 }
1019             }
1020         }
1021 
1022         mPasswordHashFile = new File(mBaseStateDir, "pwhash");
1023         if (mPasswordHashFile.exists()) {
1024             FileInputStream fin = null;
1025             DataInputStream in = null;
1026             try {
1027                 fin = new FileInputStream(mPasswordHashFile);
1028                 in = new DataInputStream(new BufferedInputStream(fin));
1029                 // integer length of the salt array, followed by the salt,
1030                 // then the hex pw hash string
1031                 int saltLen = in.readInt();
1032                 byte[] salt = new byte[saltLen];
1033                 in.readFully(salt);
1034                 mPasswordHash = in.readUTF();
1035                 mPasswordSalt = salt;
1036             } catch (IOException e) {
1037                 Slog.e(TAG, "Unable to read saved backup pw hash");
1038             } finally {
1039                 try {
1040                     if (in != null) in.close();
1041                     if (fin != null) fin.close();
1042                 } catch (IOException e) {
1043                     Slog.w(TAG, "Unable to close streams");
1044                 }
1045             }
1046         }
1047 
1048         // Alarm receivers for scheduled backups & initialization operations
1049         mRunBackupReceiver = new RunBackupReceiver();
1050         IntentFilter filter = new IntentFilter();
1051         filter.addAction(RUN_BACKUP_ACTION);
1052         context.registerReceiver(mRunBackupReceiver, filter,
1053                 android.Manifest.permission.BACKUP, null);
1054 
1055         mRunInitReceiver = new RunInitializeReceiver();
1056         filter = new IntentFilter();
1057         filter.addAction(RUN_INITIALIZE_ACTION);
1058         context.registerReceiver(mRunInitReceiver, filter,
1059                 android.Manifest.permission.BACKUP, null);
1060 
1061         Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
1062         backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1063         mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
1064 
1065         Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
1066         backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
1067         mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
1068 
1069         // Set up the backup-request journaling
1070         mJournalDir = new File(mBaseStateDir, "pending");
1071         mJournalDir.mkdirs();   // creates mBaseStateDir along the way
1072         mJournal = null;        // will be created on first use
1073 
1074         // Set up the various sorts of package tracking we do
1075         mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
1076         initPackageTracking();
1077 
1078         // Build our mapping of uid to backup client services.  This implicitly
1079         // schedules a backup pass on the Package Manager metadata the first
1080         // time anything needs to be backed up.
1081         synchronized (mBackupParticipants) {
1082             addPackageParticipantsLocked(null);
1083         }
1084 
1085         // Set up our transport options and initialize the default transport
1086         // TODO: Don't create transports that we don't need to?
1087         mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
1088                 Settings.Secure.BACKUP_TRANSPORT);
1089         if ("".equals(mCurrentTransport)) {
1090             mCurrentTransport = null;
1091         }
1092         if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
1093 
1094         // Find all transport hosts and bind to their services
1095         List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
1096                 mTransportServiceIntent, 0, UserHandle.USER_OWNER);
1097         if (DEBUG) {
1098             Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
1099         }
1100         if (hosts != null) {
1101             for (int i = 0; i < hosts.size(); i++) {
1102                 final ServiceInfo transport = hosts.get(i).serviceInfo;
1103                 if (MORE_DEBUG) {
1104                     Slog.v(TAG, "   " + transport.packageName + "/" + transport.name);
1105                 }
1106                 tryBindTransport(transport);
1107             }
1108         }
1109 
1110         // Now that we know about valid backup participants, parse any
1111         // leftover journal files into the pending backup set
1112         parseLeftoverJournals();
1113 
1114         // Power management
1115         mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
1116     }
1117 
1118     private class RunBackupReceiver extends BroadcastReceiver {
onReceive(Context context, Intent intent)1119         public void onReceive(Context context, Intent intent) {
1120             if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
1121                 synchronized (mQueueLock) {
1122                     if (mPendingInits.size() > 0) {
1123                         // If there are pending init operations, we process those
1124                         // and then settle into the usual periodic backup schedule.
1125                         if (MORE_DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
1126                         try {
1127                             mAlarmManager.cancel(mRunInitIntent);
1128                             mRunInitIntent.send();
1129                         } catch (PendingIntent.CanceledException ce) {
1130                             Slog.e(TAG, "Run init intent cancelled");
1131                             // can't really do more than bail here
1132                         }
1133                     } else {
1134                         // Don't run backups now if we're disabled or not yet
1135                         // fully set up.
1136                         if (mEnabled && mProvisioned) {
1137                             if (!mBackupRunning) {
1138                                 if (DEBUG) Slog.v(TAG, "Running a backup pass");
1139 
1140                                 // Acquire the wakelock and pass it to the backup thread.  it will
1141                                 // be released once backup concludes.
1142                                 mBackupRunning = true;
1143                                 mWakelock.acquire();
1144 
1145                                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
1146                                 mBackupHandler.sendMessage(msg);
1147                             } else {
1148                                 Slog.i(TAG, "Backup time but one already running");
1149                             }
1150                         } else {
1151                             Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
1152                         }
1153                     }
1154                 }
1155             }
1156         }
1157     }
1158 
1159     private class RunInitializeReceiver extends BroadcastReceiver {
onReceive(Context context, Intent intent)1160         public void onReceive(Context context, Intent intent) {
1161             if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
1162                 synchronized (mQueueLock) {
1163                     if (DEBUG) Slog.v(TAG, "Running a device init");
1164 
1165                     // Acquire the wakelock and pass it to the init thread.  it will
1166                     // be released once init concludes.
1167                     mWakelock.acquire();
1168 
1169                     Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
1170                     mBackupHandler.sendMessage(msg);
1171                 }
1172             }
1173         }
1174     }
1175 
initPackageTracking()1176     private void initPackageTracking() {
1177         if (MORE_DEBUG) Slog.v(TAG, "` tracking");
1178 
1179         // Remember our ancestral dataset
1180         mTokenFile = new File(mBaseStateDir, "ancestral");
1181         try {
1182             RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
1183             int version = tf.readInt();
1184             if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
1185                 mAncestralToken = tf.readLong();
1186                 mCurrentToken = tf.readLong();
1187 
1188                 int numPackages = tf.readInt();
1189                 if (numPackages >= 0) {
1190                     mAncestralPackages = new HashSet<String>();
1191                     for (int i = 0; i < numPackages; i++) {
1192                         String pkgName = tf.readUTF();
1193                         mAncestralPackages.add(pkgName);
1194                     }
1195                 }
1196             }
1197             tf.close();
1198         } catch (FileNotFoundException fnf) {
1199             // Probably innocuous
1200             Slog.v(TAG, "No ancestral data");
1201         } catch (IOException e) {
1202             Slog.w(TAG, "Unable to read token file", e);
1203         }
1204 
1205         // Keep a log of what apps we've ever backed up.  Because we might have
1206         // rebooted in the middle of an operation that was removing something from
1207         // this log, we sanity-check its contents here and reconstruct it.
1208         mEverStored = new File(mBaseStateDir, "processed");
1209         File tempProcessedFile = new File(mBaseStateDir, "processed.new");
1210 
1211         // If we were in the middle of removing something from the ever-backed-up
1212         // file, there might be a transient "processed.new" file still present.
1213         // Ignore it -- we'll validate "processed" against the current package set.
1214         if (tempProcessedFile.exists()) {
1215             tempProcessedFile.delete();
1216         }
1217 
1218         // If there are previous contents, parse them out then start a new
1219         // file to continue the recordkeeping.
1220         if (mEverStored.exists()) {
1221             RandomAccessFile temp = null;
1222             RandomAccessFile in = null;
1223 
1224             try {
1225                 temp = new RandomAccessFile(tempProcessedFile, "rws");
1226                 in = new RandomAccessFile(mEverStored, "r");
1227 
1228                 // Loop until we hit EOF
1229                 while (true) {
1230                     String pkg = in.readUTF();
1231                     try {
1232                         // is this package still present?
1233                         mPackageManager.getPackageInfo(pkg, 0);
1234                         // if we get here then yes it is; remember it
1235                         mEverStoredApps.add(pkg);
1236                         temp.writeUTF(pkg);
1237                         if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
1238                     } catch (NameNotFoundException e) {
1239                         // nope, this package was uninstalled; don't include it
1240                         if (MORE_DEBUG) Slog.v(TAG, "   - " + pkg);
1241                     }
1242                 }
1243             } catch (EOFException e) {
1244                 // Once we've rewritten the backup history log, atomically replace the
1245                 // old one with the new one then reopen the file for continuing use.
1246                 if (!tempProcessedFile.renameTo(mEverStored)) {
1247                     Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
1248                 }
1249             } catch (IOException e) {
1250                 Slog.e(TAG, "Error in processed file", e);
1251             } finally {
1252                 try { if (temp != null) temp.close(); } catch (IOException e) {}
1253                 try { if (in != null) in.close(); } catch (IOException e) {}
1254             }
1255         }
1256 
1257         synchronized (mQueueLock) {
1258             // Resume the full-data backup queue
1259             mFullBackupQueue = readFullBackupSchedule();
1260         }
1261 
1262         // Register for broadcasts about package install, etc., so we can
1263         // update the provider list.
1264         IntentFilter filter = new IntentFilter();
1265         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
1266         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1267         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1268         filter.addDataScheme("package");
1269         mContext.registerReceiver(mBroadcastReceiver, filter);
1270         // Register for events related to sdcard installation.
1271         IntentFilter sdFilter = new IntentFilter();
1272         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
1273         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1274         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
1275     }
1276 
readFullBackupSchedule()1277     private ArrayList<FullBackupEntry> readFullBackupSchedule() {
1278         boolean changed = false;
1279         ArrayList<FullBackupEntry> schedule = null;
1280         List<PackageInfo> apps =
1281                 PackageManagerBackupAgent.getStorableApplications(mPackageManager);
1282 
1283         if (mFullBackupScheduleFile.exists()) {
1284             FileInputStream fstream = null;
1285             BufferedInputStream bufStream = null;
1286             DataInputStream in = null;
1287             try {
1288                 fstream = new FileInputStream(mFullBackupScheduleFile);
1289                 bufStream = new BufferedInputStream(fstream);
1290                 in = new DataInputStream(bufStream);
1291 
1292                 int version = in.readInt();
1293                 if (version != SCHEDULE_FILE_VERSION) {
1294                     Slog.e(TAG, "Unknown backup schedule version " + version);
1295                     return null;
1296                 }
1297 
1298                 final int N = in.readInt();
1299                 schedule = new ArrayList<FullBackupEntry>(N);
1300 
1301                 // HashSet instead of ArraySet specifically because we want the eventual
1302                 // lookups against O(hundreds) of entries to be as fast as possible, and
1303                 // we discard the set immediately after the scan so the extra memory
1304                 // overhead is transient.
1305                 HashSet<String> foundApps = new HashSet<String>(N);
1306 
1307                 for (int i = 0; i < N; i++) {
1308                     String pkgName = in.readUTF();
1309                     long lastBackup = in.readLong();
1310                     foundApps.add(pkgName); // all apps that we've addressed already
1311                     try {
1312                         PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
1313                         if (appGetsFullBackup(pkg) && appIsEligibleForBackup(pkg.applicationInfo)) {
1314                             schedule.add(new FullBackupEntry(pkgName, lastBackup));
1315                         } else {
1316                             if (DEBUG) {
1317                                 Slog.i(TAG, "Package " + pkgName
1318                                         + " no longer eligible for full backup");
1319                             }
1320                         }
1321                     } catch (NameNotFoundException e) {
1322                         if (DEBUG) {
1323                             Slog.i(TAG, "Package " + pkgName
1324                                     + " not installed; dropping from full backup");
1325                         }
1326                     }
1327                 }
1328 
1329                 // New apps can arrive "out of band" via OTA and similar, so we also need to
1330                 // scan to make sure that we're tracking all full-backup candidates properly
1331                 for (PackageInfo app : apps) {
1332                     if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
1333                         if (!foundApps.contains(app.packageName)) {
1334                             if (MORE_DEBUG) {
1335                                 Slog.i(TAG, "New full backup app " + app.packageName + " found");
1336                             }
1337                             schedule.add(new FullBackupEntry(app.packageName, 0));
1338                             changed = true;
1339                         }
1340                     }
1341                 }
1342 
1343                 Collections.sort(schedule);
1344             } catch (Exception e) {
1345                 Slog.e(TAG, "Unable to read backup schedule", e);
1346                 mFullBackupScheduleFile.delete();
1347                 schedule = null;
1348             } finally {
1349                 IoUtils.closeQuietly(in);
1350                 IoUtils.closeQuietly(bufStream);
1351                 IoUtils.closeQuietly(fstream);
1352             }
1353         }
1354 
1355         if (schedule == null) {
1356             // no prior queue record, or unable to read it.  Set up the queue
1357             // from scratch.
1358             changed = true;
1359             schedule = new ArrayList<FullBackupEntry>(apps.size());
1360             for (PackageInfo info : apps) {
1361                 if (appGetsFullBackup(info) && appIsEligibleForBackup(info.applicationInfo)) {
1362                     schedule.add(new FullBackupEntry(info.packageName, 0));
1363                 }
1364             }
1365         }
1366 
1367         if (changed) {
1368             writeFullBackupScheduleAsync();
1369         }
1370         return schedule;
1371     }
1372 
1373     Runnable mFullBackupScheduleWriter = new Runnable() {
1374         @Override public void run() {
1375             synchronized (mQueueLock) {
1376                 try {
1377                     ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
1378                     DataOutputStream bufOut = new DataOutputStream(bufStream);
1379                     bufOut.writeInt(SCHEDULE_FILE_VERSION);
1380 
1381                     // version 1:
1382                     //
1383                     // [int] # of packages in the queue = N
1384                     // N * {
1385                     //     [utf8] package name
1386                     //     [long] last backup time for this package
1387                     //     }
1388                     int N = mFullBackupQueue.size();
1389                     bufOut.writeInt(N);
1390 
1391                     for (int i = 0; i < N; i++) {
1392                         FullBackupEntry entry = mFullBackupQueue.get(i);
1393                         bufOut.writeUTF(entry.packageName);
1394                         bufOut.writeLong(entry.lastBackup);
1395                     }
1396                     bufOut.flush();
1397 
1398                     AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
1399                     FileOutputStream out = af.startWrite();
1400                     out.write(bufStream.toByteArray());
1401                     af.finishWrite(out);
1402                 } catch (Exception e) {
1403                     Slog.e(TAG, "Unable to write backup schedule!", e);
1404                 }
1405             }
1406         }
1407     };
1408 
writeFullBackupScheduleAsync()1409     private void writeFullBackupScheduleAsync() {
1410         mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
1411         mBackupHandler.post(mFullBackupScheduleWriter);
1412     }
1413 
parseLeftoverJournals()1414     private void parseLeftoverJournals() {
1415         for (File f : mJournalDir.listFiles()) {
1416             if (mJournal == null || f.compareTo(mJournal) != 0) {
1417                 // This isn't the current journal, so it must be a leftover.  Read
1418                 // out the package names mentioned there and schedule them for
1419                 // backup.
1420                 RandomAccessFile in = null;
1421                 try {
1422                     Slog.i(TAG, "Found stale backup journal, scheduling");
1423                     in = new RandomAccessFile(f, "r");
1424                     while (true) {
1425                         String packageName = in.readUTF();
1426                         if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
1427                         dataChangedImpl(packageName);
1428                     }
1429                 } catch (EOFException e) {
1430                     // no more data; we're done
1431                 } catch (Exception e) {
1432                     Slog.e(TAG, "Can't read " + f, e);
1433                 } finally {
1434                     // close/delete the file
1435                     try { if (in != null) in.close(); } catch (IOException e) {}
1436                     f.delete();
1437                 }
1438             }
1439         }
1440     }
1441 
buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds)1442     private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) {
1443         return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds);
1444     }
1445 
buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds)1446     private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) {
1447         try {
1448             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
1449             KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
1450             return keyFactory.generateSecret(ks);
1451         } catch (InvalidKeySpecException e) {
1452             Slog.e(TAG, "Invalid key spec for PBKDF2!");
1453         } catch (NoSuchAlgorithmException e) {
1454             Slog.e(TAG, "PBKDF2 unavailable!");
1455         }
1456         return null;
1457     }
1458 
buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds)1459     private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) {
1460         SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds);
1461         if (key != null) {
1462             return byteArrayToHex(key.getEncoded());
1463         }
1464         return null;
1465     }
1466 
byteArrayToHex(byte[] data)1467     private String byteArrayToHex(byte[] data) {
1468         StringBuilder buf = new StringBuilder(data.length * 2);
1469         for (int i = 0; i < data.length; i++) {
1470             buf.append(Byte.toHexString(data[i], true));
1471         }
1472         return buf.toString();
1473     }
1474 
hexToByteArray(String digits)1475     private byte[] hexToByteArray(String digits) {
1476         final int bytes = digits.length() / 2;
1477         if (2*bytes != digits.length()) {
1478             throw new IllegalArgumentException("Hex string must have an even number of digits");
1479         }
1480 
1481         byte[] result = new byte[bytes];
1482         for (int i = 0; i < digits.length(); i += 2) {
1483             result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16);
1484         }
1485         return result;
1486     }
1487 
makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds)1488     private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) {
1489         char[] mkAsChar = new char[pwBytes.length];
1490         for (int i = 0; i < pwBytes.length; i++) {
1491             mkAsChar[i] = (char) pwBytes[i];
1492         }
1493 
1494         Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds);
1495         return checksum.getEncoded();
1496     }
1497 
1498     // Used for generating random salts or passwords
randomBytes(int bits)1499     private byte[] randomBytes(int bits) {
1500         byte[] array = new byte[bits / 8];
1501         mRng.nextBytes(array);
1502         return array;
1503     }
1504 
passwordMatchesSaved(String algorithm, String candidatePw, int rounds)1505     boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) {
1506         if (mPasswordHash == null) {
1507             // no current password case -- require that 'currentPw' be null or empty
1508             if (candidatePw == null || "".equals(candidatePw)) {
1509                 return true;
1510             } // else the non-empty candidate does not match the empty stored pw
1511         } else {
1512             // hash the stated current pw and compare to the stored one
1513             if (candidatePw != null && candidatePw.length() > 0) {
1514                 String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds);
1515                 if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
1516                     // candidate hash matches the stored hash -- the password matches
1517                     return true;
1518                 }
1519             } // else the stored pw is nonempty but the candidate is empty; no match
1520         }
1521         return false;
1522     }
1523 
setBackupPassword(String currentPw, String newPw)1524     public boolean setBackupPassword(String currentPw, String newPw) {
1525         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1526                 "setBackupPassword");
1527 
1528         // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes
1529         final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
1530 
1531         // If the supplied pw doesn't hash to the the saved one, fail.  The password
1532         // might be caught in the legacy crypto mismatch; verify that too.
1533         if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
1534                 && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
1535                         currentPw, PBKDF2_HASH_ROUNDS))) {
1536             return false;
1537         }
1538 
1539         // Snap up to current on the pw file version
1540         mPasswordVersion = BACKUP_PW_FILE_VERSION;
1541         FileOutputStream pwFout = null;
1542         DataOutputStream pwOut = null;
1543         try {
1544             pwFout = new FileOutputStream(mPasswordVersionFile);
1545             pwOut = new DataOutputStream(pwFout);
1546             pwOut.writeInt(mPasswordVersion);
1547         } catch (IOException e) {
1548             Slog.e(TAG, "Unable to write backup pw version; password not changed");
1549             return false;
1550         } finally {
1551             try {
1552                 if (pwOut != null) pwOut.close();
1553                 if (pwFout != null) pwFout.close();
1554             } catch (IOException e) {
1555                 Slog.w(TAG, "Unable to close pw version record");
1556             }
1557         }
1558 
1559         // Clearing the password is okay
1560         if (newPw == null || newPw.isEmpty()) {
1561             if (mPasswordHashFile.exists()) {
1562                 if (!mPasswordHashFile.delete()) {
1563                     // Unable to delete the old pw file, so fail
1564                     Slog.e(TAG, "Unable to clear backup password");
1565                     return false;
1566                 }
1567             }
1568             mPasswordHash = null;
1569             mPasswordSalt = null;
1570             return true;
1571         }
1572 
1573         try {
1574             // Okay, build the hash of the new backup password
1575             byte[] salt = randomBytes(PBKDF2_SALT_SIZE);
1576             String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS);
1577 
1578             OutputStream pwf = null, buffer = null;
1579             DataOutputStream out = null;
1580             try {
1581                 pwf = new FileOutputStream(mPasswordHashFile);
1582                 buffer = new BufferedOutputStream(pwf);
1583                 out = new DataOutputStream(buffer);
1584                 // integer length of the salt array, followed by the salt,
1585                 // then the hex pw hash string
1586                 out.writeInt(salt.length);
1587                 out.write(salt);
1588                 out.writeUTF(newPwHash);
1589                 out.flush();
1590                 mPasswordHash = newPwHash;
1591                 mPasswordSalt = salt;
1592                 return true;
1593             } finally {
1594                 if (out != null) out.close();
1595                 if (buffer != null) buffer.close();
1596                 if (pwf != null) pwf.close();
1597             }
1598         } catch (IOException e) {
1599             Slog.e(TAG, "Unable to set backup password");
1600         }
1601         return false;
1602     }
1603 
hasBackupPassword()1604     public boolean hasBackupPassword() {
1605         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
1606                 "hasBackupPassword");
1607 
1608         return mPasswordHash != null && mPasswordHash.length() > 0;
1609     }
1610 
backupPasswordMatches(String currentPw)1611     private boolean backupPasswordMatches(String currentPw) {
1612         if (hasBackupPassword()) {
1613             final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
1614             if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
1615                     && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
1616                             currentPw, PBKDF2_HASH_ROUNDS))) {
1617                 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
1618                 return false;
1619             }
1620         }
1621         return true;
1622     }
1623 
1624     // Maintain persistent state around whether need to do an initialize operation.
1625     // Must be called with the queue lock held.
recordInitPendingLocked(boolean isPending, String transportName)1626     void recordInitPendingLocked(boolean isPending, String transportName) {
1627         if (MORE_DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
1628                 + " on transport " + transportName);
1629         mBackupHandler.removeMessages(MSG_RETRY_INIT);
1630 
1631         try {
1632             IBackupTransport transport = getTransport(transportName);
1633             if (transport != null) {
1634                 String transportDirName = transport.transportDirName();
1635                 File stateDir = new File(mBaseStateDir, transportDirName);
1636                 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1637 
1638                 if (isPending) {
1639                     // We need an init before we can proceed with sending backup data.
1640                     // Record that with an entry in our set of pending inits, as well as
1641                     // journaling it via creation of a sentinel file.
1642                     mPendingInits.add(transportName);
1643                     try {
1644                         (new FileOutputStream(initPendingFile)).close();
1645                     } catch (IOException ioe) {
1646                         // Something is badly wrong with our permissions; just try to move on
1647                     }
1648                 } else {
1649                     // No more initialization needed; wipe the journal and reset our state.
1650                     initPendingFile.delete();
1651                     mPendingInits.remove(transportName);
1652                 }
1653                 return; // done; don't fall through to the error case
1654             }
1655         } catch (RemoteException e) {
1656             // transport threw when asked its name; fall through to the lookup-failed case
1657         }
1658 
1659         // The named transport doesn't exist or threw.  This operation is
1660         // important, so we record the need for a an init and post a message
1661         // to retry the init later.
1662         if (isPending) {
1663             mPendingInits.add(transportName);
1664             mBackupHandler.sendMessageDelayed(
1665                     mBackupHandler.obtainMessage(MSG_RETRY_INIT,
1666                             (isPending ? 1 : 0),
1667                             0,
1668                             transportName),
1669                     TRANSPORT_RETRY_INTERVAL);
1670         }
1671     }
1672 
1673     // Reset all of our bookkeeping, in response to having been told that
1674     // the backend data has been wiped [due to idle expiry, for example],
1675     // so we must re-upload all saved settings.
resetBackupState(File stateFileDir)1676     void resetBackupState(File stateFileDir) {
1677         synchronized (mQueueLock) {
1678             // Wipe the "what we've ever backed up" tracking
1679             mEverStoredApps.clear();
1680             mEverStored.delete();
1681 
1682             mCurrentToken = 0;
1683             writeRestoreTokens();
1684 
1685             // Remove all the state files
1686             for (File sf : stateFileDir.listFiles()) {
1687                 // ... but don't touch the needs-init sentinel
1688                 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
1689                     sf.delete();
1690                 }
1691             }
1692         }
1693 
1694         // Enqueue a new backup of every participant
1695         synchronized (mBackupParticipants) {
1696             final int N = mBackupParticipants.size();
1697             for (int i=0; i<N; i++) {
1698                 HashSet<String> participants = mBackupParticipants.valueAt(i);
1699                 if (participants != null) {
1700                     for (String packageName : participants) {
1701                         dataChangedImpl(packageName);
1702                     }
1703                 }
1704             }
1705         }
1706     }
1707 
1708     // Add a transport to our set of available backends.  If 'transport' is null, this
1709     // is an unregistration, and the transport's entry is removed from our bookkeeping.
registerTransport(String name, String component, IBackupTransport transport)1710     private void registerTransport(String name, String component, IBackupTransport transport) {
1711         synchronized (mTransports) {
1712             if (DEBUG) Slog.v(TAG, "Registering transport "
1713                     + component + "::" + name + " = " + transport);
1714             if (transport != null) {
1715                 mTransports.put(name, transport);
1716                 mTransportNames.put(component, name);
1717             } else {
1718                 mTransports.remove(mTransportNames.get(component));
1719                 mTransportNames.remove(component);
1720                 // Nothing further to do in the unregistration case
1721                 return;
1722             }
1723         }
1724 
1725         // If the init sentinel file exists, we need to be sure to perform the init
1726         // as soon as practical.  We also create the state directory at registration
1727         // time to ensure it's present from the outset.
1728         try {
1729             String transportName = transport.transportDirName();
1730             File stateDir = new File(mBaseStateDir, transportName);
1731             stateDir.mkdirs();
1732 
1733             File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
1734             if (initSentinel.exists()) {
1735                 synchronized (mQueueLock) {
1736                     mPendingInits.add(transportName);
1737 
1738                     // TODO: pick a better starting time than now + 1 minute
1739                     long delay = 1000 * 60; // one minute, in milliseconds
1740                     mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1741                             System.currentTimeMillis() + delay, mRunInitIntent);
1742                 }
1743             }
1744         } catch (RemoteException e) {
1745             // the transport threw when asked its file naming prefs; declare it invalid
1746             Slog.e(TAG, "Unable to register transport as " + name);
1747             mTransportNames.remove(component);
1748             mTransports.remove(name);
1749         }
1750     }
1751 
1752     // ----- Track installation/removal of packages -----
1753     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1754         public void onReceive(Context context, Intent intent) {
1755             if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
1756 
1757             String action = intent.getAction();
1758             boolean replacing = false;
1759             boolean added = false;
1760             boolean changed = false;
1761             Bundle extras = intent.getExtras();
1762             String pkgList[] = null;
1763             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
1764                     Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
1765                     Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
1766                 Uri uri = intent.getData();
1767                 if (uri == null) {
1768                     return;
1769                 }
1770                 String pkgName = uri.getSchemeSpecificPart();
1771                 if (pkgName != null) {
1772                     pkgList = new String[] { pkgName };
1773                 }
1774                 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
1775 
1776                 // At package-changed we only care about looking at new transport states
1777                 if (changed) {
1778                     try {
1779                         String[] components =
1780                                 intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
1781 
1782                         if (MORE_DEBUG) {
1783                             Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
1784                             for (int i = 0; i < components.length; i++) {
1785                                 Slog.i(TAG, "   * " + components[i]);
1786                             }
1787                         }
1788 
1789                         // In general we need to try to bind any time we see a component enable
1790                         // state change, because that change may have made a transport available.
1791                         // However, because we currently only support a single transport component
1792                         // per package, we can skip the bind attempt if the change (a) affects a
1793                         // package known to host a transport, but (b) does not affect the known
1794                         // transport component itself.
1795                         //
1796                         // In addition, if the change *is* to a known transport component, we need
1797                         // to unbind it before retrying the binding.
1798                         boolean tryBind = true;
1799                         synchronized (mTransports) {
1800                             TransportConnection conn = mTransportConnections.get(pkgName);
1801                             if (conn != null) {
1802                                 // We have a bound transport in this package; do we need to rebind it?
1803                                 final ServiceInfo svc = conn.mTransport;
1804                                 ComponentName svcName =
1805                                         new ComponentName(svc.packageName, svc.name);
1806                                 if (svc.packageName.equals(pkgName)) {
1807                                     final String className = svcName.getClassName();
1808                                     if (MORE_DEBUG) {
1809                                         Slog.i(TAG, "Checking need to rebind " + className);
1810                                     }
1811                                     // See whether it's the transport component within this package
1812                                     boolean isTransport = false;
1813                                     for (int i = 0; i < components.length; i++) {
1814                                         if (className.equals(components[i])) {
1815                                             // Okay, it's an existing transport component.
1816                                             final String flatName = svcName.flattenToShortString();
1817                                             mContext.unbindService(conn);
1818                                             mTransportConnections.remove(pkgName);
1819                                             mTransports.remove(mTransportNames.get(flatName));
1820                                             mTransportNames.remove(flatName);
1821                                             isTransport = true;
1822                                             break;
1823                                         }
1824                                     }
1825                                     if (!isTransport) {
1826                                         // A non-transport component within a package that is hosting
1827                                         // a bound transport
1828                                         tryBind = false;
1829                                     }
1830                                 }
1831                             }
1832                         }
1833                         // and now (re)bind as appropriate
1834                         if (tryBind) {
1835                             if (MORE_DEBUG) {
1836                                 Slog.i(TAG, "Yes, need to recheck binding");
1837                             }
1838                             PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0);
1839                             checkForTransportAndBind(app);
1840                         }
1841                     } catch (NameNotFoundException e) {
1842                         // Nope, can't find it - just ignore
1843                         if (MORE_DEBUG) {
1844                             Slog.w(TAG, "Can't find changed package " + pkgName);
1845                         }
1846                     }
1847                     return; // nothing more to do in the PACKAGE_CHANGED case
1848                 }
1849 
1850                 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
1851                 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
1852             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
1853                 added = true;
1854                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1855             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
1856                 added = false;
1857                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1858             }
1859 
1860             if (pkgList == null || pkgList.length == 0) {
1861                 return;
1862             }
1863 
1864             final int uid = extras.getInt(Intent.EXTRA_UID);
1865             if (added) {
1866                 synchronized (mBackupParticipants) {
1867                     if (replacing) {
1868                         // This is the package-replaced case; we just remove the entry
1869                         // under the old uid and fall through to re-add.
1870                         removePackageParticipantsLocked(pkgList, uid);
1871                     }
1872                     addPackageParticipantsLocked(pkgList);
1873                 }
1874                 // If they're full-backup candidates, add them there instead
1875                 final long now = System.currentTimeMillis();
1876                 for (String packageName : pkgList) {
1877                     try {
1878                         PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
1879                         if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
1880                             enqueueFullBackup(packageName, now);
1881                             scheduleNextFullBackupJob(0);
1882                         }
1883 
1884                         // Transport maintenance: rebind to known existing transports that have
1885                         // just been updated; and bind to any newly-installed transport services.
1886                         synchronized (mTransports) {
1887                             final TransportConnection conn = mTransportConnections.get(packageName);
1888                             if (conn != null) {
1889                                 if (MORE_DEBUG) {
1890                                     Slog.i(TAG, "Transport package changed; rebinding");
1891                                 }
1892                                 bindTransport(conn.mTransport);
1893                             } else {
1894                                 checkForTransportAndBind(app);
1895                             }
1896                         }
1897 
1898                     } catch (NameNotFoundException e) {
1899                         // doesn't really exist; ignore it
1900                         if (DEBUG) {
1901                             Slog.w(TAG, "Can't resolve new app " + packageName);
1902                         }
1903                     }
1904                 }
1905 
1906                 // Whenever a package is added or updated we need to update
1907                 // the package metadata bookkeeping.
1908                 dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
1909             } else {
1910                 if (replacing) {
1911                     // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
1912                 } else {
1913                     synchronized (mBackupParticipants) {
1914                         removePackageParticipantsLocked(pkgList, uid);
1915                     }
1916                 }
1917             }
1918         }
1919     };
1920 
1921     // ----- Track connection to transports service -----
1922     class TransportConnection implements ServiceConnection {
1923         ServiceInfo mTransport;
1924 
TransportConnection(ServiceInfo transport)1925         public TransportConnection(ServiceInfo transport) {
1926             mTransport = transport;
1927         }
1928 
1929         @Override
onServiceConnected(ComponentName component, IBinder service)1930         public void onServiceConnected(ComponentName component, IBinder service) {
1931             if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
1932             final String name = component.flattenToShortString();
1933             try {
1934                 IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
1935                 registerTransport(transport.name(), name, transport);
1936                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1);
1937             } catch (RemoteException e) {
1938                 Slog.e(TAG, "Unable to register transport " + component);
1939                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
1940             }
1941         }
1942 
1943         @Override
onServiceDisconnected(ComponentName component)1944         public void onServiceDisconnected(ComponentName component) {
1945             if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component);
1946             final String name = component.flattenToShortString();
1947             EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
1948             registerTransport(null, name, null);
1949         }
1950     };
1951 
1952     // Check whether the given package hosts a transport, and bind if so
checkForTransportAndBind(PackageInfo pkgInfo)1953     void checkForTransportAndBind(PackageInfo pkgInfo) {
1954         Intent intent = new Intent(mTransportServiceIntent)
1955                 .setPackage(pkgInfo.packageName);
1956         List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
1957                 intent, 0, UserHandle.USER_OWNER);
1958         if (hosts != null) {
1959             final int N = hosts.size();
1960             for (int i = 0; i < N; i++) {
1961                 final ServiceInfo info = hosts.get(i).serviceInfo;
1962                 tryBindTransport(info);
1963             }
1964         }
1965     }
1966 
1967     // Verify that the service exists and is hosted by a privileged app, then proceed to bind
tryBindTransport(ServiceInfo info)1968     boolean tryBindTransport(ServiceInfo info) {
1969         try {
1970             PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
1971             if ((packInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
1972                     != 0) {
1973                 return bindTransport(info);
1974             } else {
1975                 Slog.w(TAG, "Transport package " + info.packageName + " not privileged");
1976             }
1977         } catch (NameNotFoundException e) {
1978             Slog.w(TAG, "Problem resolving transport package " + info.packageName);
1979         }
1980         return false;
1981     }
1982 
1983     // Actually bind; presumes that we have already validated the transport service
bindTransport(ServiceInfo transport)1984     boolean bindTransport(ServiceInfo transport) {
1985         ComponentName svcName = new ComponentName(transport.packageName, transport.name);
1986         if (MORE_DEBUG) {
1987             Slog.i(TAG, "Binding to transport host " + svcName);
1988         }
1989         Intent intent = new Intent(mTransportServiceIntent);
1990         intent.setComponent(svcName);
1991 
1992         TransportConnection connection;
1993         synchronized (mTransports) {
1994             connection = mTransportConnections.get(transport.packageName);
1995             if (null == connection) {
1996                 connection = new TransportConnection(transport);
1997                 mTransportConnections.put(transport.packageName, connection);
1998             } else {
1999                 // This is a rebind due to package upgrade.  The service won't be
2000                 // automatically relaunched for us until we explicitly rebind, but
2001                 // we need to unbind the now-orphaned original connection.
2002                 mContext.unbindService(connection);
2003             }
2004         }
2005         return mContext.bindServiceAsUser(intent,
2006                 connection, Context.BIND_AUTO_CREATE,
2007                 UserHandle.OWNER);
2008     }
2009 
2010     // Add the backup agents in the given packages to our set of known backup participants.
2011     // If 'packageNames' is null, adds all backup agents in the whole system.
addPackageParticipantsLocked(String[] packageNames)2012     void addPackageParticipantsLocked(String[] packageNames) {
2013         // Look for apps that define the android:backupAgent attribute
2014         List<PackageInfo> targetApps = allAgentPackages();
2015         if (packageNames != null) {
2016             if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
2017             for (String packageName : packageNames) {
2018                 addPackageParticipantsLockedInner(packageName, targetApps);
2019             }
2020         } else {
2021             if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
2022             addPackageParticipantsLockedInner(null, targetApps);
2023         }
2024     }
2025 
addPackageParticipantsLockedInner(String packageName, List<PackageInfo> targetPkgs)2026     private void addPackageParticipantsLockedInner(String packageName,
2027             List<PackageInfo> targetPkgs) {
2028         if (MORE_DEBUG) {
2029             Slog.v(TAG, "Examining " + packageName + " for backup agent");
2030         }
2031 
2032         for (PackageInfo pkg : targetPkgs) {
2033             if (packageName == null || pkg.packageName.equals(packageName)) {
2034                 int uid = pkg.applicationInfo.uid;
2035                 HashSet<String> set = mBackupParticipants.get(uid);
2036                 if (set == null) {
2037                     set = new HashSet<String>();
2038                     mBackupParticipants.put(uid, set);
2039                 }
2040                 set.add(pkg.packageName);
2041                 if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
2042 
2043                 // Schedule a backup for it on general principles
2044                 if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
2045                 dataChangedImpl(pkg.packageName);
2046             }
2047         }
2048     }
2049 
2050     // Remove the given packages' entries from our known active set.
removePackageParticipantsLocked(String[] packageNames, int oldUid)2051     void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
2052         if (packageNames == null) {
2053             Slog.w(TAG, "removePackageParticipants with null list");
2054             return;
2055         }
2056 
2057         if (MORE_DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
2058                 + " #" + packageNames.length);
2059         for (String pkg : packageNames) {
2060             // Known previous UID, so we know which package set to check
2061             HashSet<String> set = mBackupParticipants.get(oldUid);
2062             if (set != null && set.contains(pkg)) {
2063                 removePackageFromSetLocked(set, pkg);
2064                 if (set.isEmpty()) {
2065                     if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
2066                     mBackupParticipants.remove(oldUid);
2067                 }
2068             }
2069         }
2070     }
2071 
removePackageFromSetLocked(final HashSet<String> set, final String packageName)2072     private void removePackageFromSetLocked(final HashSet<String> set,
2073             final String packageName) {
2074         if (set.contains(packageName)) {
2075             // Found it.  Remove this one package from the bookkeeping, and
2076             // if it's the last participating app under this uid we drop the
2077             // (now-empty) set as well.
2078             // Note that we deliberately leave it 'known' in the "ever backed up"
2079             // bookkeeping so that its current-dataset data will be retrieved
2080             // if the app is subsequently reinstalled
2081             if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
2082             set.remove(packageName);
2083             mPendingBackups.remove(packageName);
2084         }
2085     }
2086 
2087     // Returns the set of all applications that define an android:backupAgent attribute
allAgentPackages()2088     List<PackageInfo> allAgentPackages() {
2089         // !!! TODO: cache this and regenerate only when necessary
2090         int flags = PackageManager.GET_SIGNATURES;
2091         List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
2092         int N = packages.size();
2093         for (int a = N-1; a >= 0; a--) {
2094             PackageInfo pkg = packages.get(a);
2095             try {
2096                 ApplicationInfo app = pkg.applicationInfo;
2097                 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
2098                         || app.backupAgentName == null) {
2099                     packages.remove(a);
2100                 }
2101                 else {
2102                     // we will need the shared library path, so look that up and store it here.
2103                     // This is used implicitly when we pass the PackageInfo object off to
2104                     // the Activity Manager to launch the app for backup/restore purposes.
2105                     app = mPackageManager.getApplicationInfo(pkg.packageName,
2106                             PackageManager.GET_SHARED_LIBRARY_FILES);
2107                     pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
2108                 }
2109             } catch (NameNotFoundException e) {
2110                 packages.remove(a);
2111             }
2112         }
2113         return packages;
2114     }
2115 
2116     // Called from the backup tasks: record that the given app has been successfully
2117     // backed up at least once.  This includes both key/value and full-data backups
2118     // through the transport.
logBackupComplete(String packageName)2119     void logBackupComplete(String packageName) {
2120         if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
2121 
2122         synchronized (mEverStoredApps) {
2123             if (!mEverStoredApps.add(packageName)) return;
2124 
2125             RandomAccessFile out = null;
2126             try {
2127                 out = new RandomAccessFile(mEverStored, "rws");
2128                 out.seek(out.length());
2129                 out.writeUTF(packageName);
2130             } catch (IOException e) {
2131                 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
2132             } finally {
2133                 try { if (out != null) out.close(); } catch (IOException e) {}
2134             }
2135         }
2136     }
2137 
2138     // Remove our awareness of having ever backed up the given package
removeEverBackedUp(String packageName)2139     void removeEverBackedUp(String packageName) {
2140         if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
2141         if (MORE_DEBUG) Slog.v(TAG, "New set:");
2142 
2143         synchronized (mEverStoredApps) {
2144             // Rewrite the file and rename to overwrite.  If we reboot in the middle,
2145             // we'll recognize on initialization time that the package no longer
2146             // exists and fix it up then.
2147             File tempKnownFile = new File(mBaseStateDir, "processed.new");
2148             RandomAccessFile known = null;
2149             try {
2150                 known = new RandomAccessFile(tempKnownFile, "rws");
2151                 mEverStoredApps.remove(packageName);
2152                 for (String s : mEverStoredApps) {
2153                     known.writeUTF(s);
2154                     if (MORE_DEBUG) Slog.v(TAG, "    " + s);
2155                 }
2156                 known.close();
2157                 known = null;
2158                 if (!tempKnownFile.renameTo(mEverStored)) {
2159                     throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
2160                 }
2161             } catch (IOException e) {
2162                 // Bad: we couldn't create the new copy.  For safety's sake we
2163                 // abandon the whole process and remove all what's-backed-up
2164                 // state entirely, meaning we'll force a backup pass for every
2165                 // participant on the next boot or [re]install.
2166                 Slog.w(TAG, "Error rewriting " + mEverStored, e);
2167                 mEverStoredApps.clear();
2168                 tempKnownFile.delete();
2169                 mEverStored.delete();
2170             } finally {
2171                 try { if (known != null) known.close(); } catch (IOException e) {}
2172             }
2173         }
2174     }
2175 
2176     // Persistently record the current and ancestral backup tokens as well
2177     // as the set of packages with data [supposedly] available in the
2178     // ancestral dataset.
writeRestoreTokens()2179     void writeRestoreTokens() {
2180         try {
2181             RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
2182 
2183             // First, the version number of this record, for futureproofing
2184             af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
2185 
2186             // Write the ancestral and current tokens
2187             af.writeLong(mAncestralToken);
2188             af.writeLong(mCurrentToken);
2189 
2190             // Now write the set of ancestral packages
2191             if (mAncestralPackages == null) {
2192                 af.writeInt(-1);
2193             } else {
2194                 af.writeInt(mAncestralPackages.size());
2195                 if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
2196                 for (String pkgName : mAncestralPackages) {
2197                     af.writeUTF(pkgName);
2198                     if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
2199                 }
2200             }
2201             af.close();
2202         } catch (IOException e) {
2203             Slog.w(TAG, "Unable to write token file:", e);
2204         }
2205     }
2206 
2207     // Return the given transport
getTransport(String transportName)2208     private IBackupTransport getTransport(String transportName) {
2209         synchronized (mTransports) {
2210             IBackupTransport transport = mTransports.get(transportName);
2211             if (transport == null) {
2212                 Slog.w(TAG, "Requested unavailable transport: " + transportName);
2213             }
2214             return transport;
2215         }
2216     }
2217 
2218     // fire off a backup agent, blocking until it attaches or times out
bindToAgentSynchronous(ApplicationInfo app, int mode)2219     IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
2220         IBackupAgent agent = null;
2221         synchronized(mAgentConnectLock) {
2222             mConnecting = true;
2223             mConnectedAgent = null;
2224             try {
2225                 if (mActivityManager.bindBackupAgent(app, mode)) {
2226                     Slog.d(TAG, "awaiting agent for " + app);
2227 
2228                     // success; wait for the agent to arrive
2229                     // only wait 10 seconds for the bind to happen
2230                     long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
2231                     while (mConnecting && mConnectedAgent == null
2232                             && (System.currentTimeMillis() < timeoutMark)) {
2233                         try {
2234                             mAgentConnectLock.wait(5000);
2235                         } catch (InterruptedException e) {
2236                             // just bail
2237                             Slog.w(TAG, "Interrupted: " + e);
2238                             mActivityManager.clearPendingBackup();
2239                             return null;
2240                         }
2241                     }
2242 
2243                     // if we timed out with no connect, abort and move on
2244                     if (mConnecting == true) {
2245                         Slog.w(TAG, "Timeout waiting for agent " + app);
2246                         mActivityManager.clearPendingBackup();
2247                         return null;
2248                     }
2249                     if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
2250                     agent = mConnectedAgent;
2251                 }
2252             } catch (RemoteException e) {
2253                 // can't happen - ActivityManager is local
2254             }
2255         }
2256         return agent;
2257     }
2258 
2259     // clear an application's data, blocking until the operation completes or times out
clearApplicationDataSynchronous(String packageName)2260     void clearApplicationDataSynchronous(String packageName) {
2261         // Don't wipe packages marked allowClearUserData=false
2262         try {
2263             PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
2264             if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
2265                 if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
2266                         + packageName);
2267                 return;
2268             }
2269         } catch (NameNotFoundException e) {
2270             Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
2271             return;
2272         }
2273 
2274         ClearDataObserver observer = new ClearDataObserver();
2275 
2276         synchronized(mClearDataLock) {
2277             mClearingData = true;
2278             try {
2279                 mActivityManager.clearApplicationUserData(packageName, observer, 0);
2280             } catch (RemoteException e) {
2281                 // can't happen because the activity manager is in this process
2282             }
2283 
2284             // only wait 10 seconds for the clear data to happen
2285             long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
2286             while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
2287                 try {
2288                     mClearDataLock.wait(5000);
2289                 } catch (InterruptedException e) {
2290                     // won't happen, but still.
2291                     mClearingData = false;
2292                 }
2293             }
2294         }
2295     }
2296 
2297     class ClearDataObserver extends IPackageDataObserver.Stub {
onRemoveCompleted(String packageName, boolean succeeded)2298         public void onRemoveCompleted(String packageName, boolean succeeded) {
2299             synchronized(mClearDataLock) {
2300                 mClearingData = false;
2301                 mClearDataLock.notifyAll();
2302             }
2303         }
2304     }
2305 
2306     // Get the restore-set token for the best-available restore set for this package:
2307     // the active set if possible, else the ancestral one.  Returns zero if none available.
getAvailableRestoreToken(String packageName)2308     public long getAvailableRestoreToken(String packageName) {
2309         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
2310                 "getAvailableRestoreToken");
2311 
2312         long token = mAncestralToken;
2313         synchronized (mQueueLock) {
2314             if (mEverStoredApps.contains(packageName)) {
2315                 if (MORE_DEBUG) {
2316                     Slog.i(TAG, "App in ever-stored, so using current token");
2317                 }
2318                 token = mCurrentToken;
2319             }
2320         }
2321         if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
2322         return token;
2323     }
2324 
2325     // -----
2326     // Interface and methods used by the asynchronous-with-timeout backup/restore operations
2327 
2328     interface BackupRestoreTask {
2329         // Execute one tick of whatever state machine the task implements
execute()2330         void execute();
2331 
2332         // An operation that wanted a callback has completed
operationComplete(int result)2333         void operationComplete(int result);
2334 
2335         // An operation that wanted a callback has timed out
handleTimeout()2336         void handleTimeout();
2337     }
2338 
prepareOperationTimeout(int token, long interval, BackupRestoreTask callback)2339     void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) {
2340         if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
2341                 + " interval=" + interval);
2342         synchronized (mCurrentOpLock) {
2343             mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
2344 
2345             Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
2346             mBackupHandler.sendMessageDelayed(msg, interval);
2347         }
2348     }
2349 
2350     // synchronous waiter case
waitUntilOperationComplete(int token)2351     boolean waitUntilOperationComplete(int token) {
2352         if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
2353                 + Integer.toHexString(token));
2354         int finalState = OP_PENDING;
2355         Operation op = null;
2356         synchronized (mCurrentOpLock) {
2357             while (true) {
2358                 op = mCurrentOperations.get(token);
2359                 if (op == null) {
2360                     // mysterious disappearance: treat as success with no callback
2361                     break;
2362                 } else {
2363                     if (op.state == OP_PENDING) {
2364                         try {
2365                             mCurrentOpLock.wait();
2366                         } catch (InterruptedException e) {}
2367                         // When the wait is notified we loop around and recheck the current state
2368                     } else {
2369                         // No longer pending; we're done
2370                         finalState = op.state;
2371                         break;
2372                     }
2373                 }
2374             }
2375         }
2376 
2377         mBackupHandler.removeMessages(MSG_TIMEOUT);
2378         if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
2379                 + " complete: finalState=" + finalState);
2380         return finalState == OP_ACKNOWLEDGED;
2381     }
2382 
handleTimeout(int token, Object obj)2383     void handleTimeout(int token, Object obj) {
2384         // Notify any synchronous waiters
2385         Operation op = null;
2386         synchronized (mCurrentOpLock) {
2387             op = mCurrentOperations.get(token);
2388             if (MORE_DEBUG) {
2389                 if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
2390                         + " but no op found");
2391             }
2392             int state = (op != null) ? op.state : OP_TIMEOUT;
2393             if (state == OP_PENDING) {
2394                 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
2395                 op.state = OP_TIMEOUT;
2396                 mCurrentOperations.put(token, op);
2397             }
2398             mCurrentOpLock.notifyAll();
2399         }
2400 
2401         // If there's a TimeoutHandler for this event, call it
2402         if (op != null && op.callback != null) {
2403             op.callback.handleTimeout();
2404         }
2405     }
2406 
2407     // ----- Back up a set of applications via a worker thread -----
2408 
2409     enum BackupState {
2410         INITIAL,
2411         RUNNING_QUEUE,
2412         FINAL
2413     }
2414 
2415     class PerformBackupTask implements BackupRestoreTask {
2416         private static final String TAG = "PerformBackupTask";
2417 
2418         IBackupTransport mTransport;
2419         ArrayList<BackupRequest> mQueue;
2420         ArrayList<BackupRequest> mOriginalQueue;
2421         File mStateDir;
2422         File mJournal;
2423         BackupState mCurrentState;
2424 
2425         // carried information about the current in-flight operation
2426         IBackupAgent mAgentBinder;
2427         PackageInfo mCurrentPackage;
2428         File mSavedStateName;
2429         File mBackupDataName;
2430         File mNewStateName;
2431         ParcelFileDescriptor mSavedState;
2432         ParcelFileDescriptor mBackupData;
2433         ParcelFileDescriptor mNewState;
2434         int mStatus;
2435         boolean mFinished;
2436 
PerformBackupTask(IBackupTransport transport, String dirName, ArrayList<BackupRequest> queue, File journal)2437         public PerformBackupTask(IBackupTransport transport, String dirName,
2438                 ArrayList<BackupRequest> queue, File journal) {
2439             mTransport = transport;
2440             mOriginalQueue = queue;
2441             mJournal = journal;
2442 
2443             mStateDir = new File(mBaseStateDir, dirName);
2444 
2445             mCurrentState = BackupState.INITIAL;
2446             mFinished = false;
2447 
2448             addBackupTrace("STATE => INITIAL");
2449         }
2450 
2451         // Main entry point: perform one chunk of work, updating the state as appropriate
2452         // and reposting the next chunk to the primary backup handler thread.
2453         @Override
execute()2454         public void execute() {
2455             switch (mCurrentState) {
2456                 case INITIAL:
2457                     beginBackup();
2458                     break;
2459 
2460                 case RUNNING_QUEUE:
2461                     invokeNextAgent();
2462                     break;
2463 
2464                 case FINAL:
2465                     if (!mFinished) finalizeBackup();
2466                     else {
2467                         Slog.e(TAG, "Duplicate finish");
2468                     }
2469                     mFinished = true;
2470                     break;
2471             }
2472         }
2473 
2474         // We're starting a backup pass.  Initialize the transport and send
2475         // the PM metadata blob if we haven't already.
beginBackup()2476         void beginBackup() {
2477             if (DEBUG_BACKUP_TRACE) {
2478                 clearBackupTrace();
2479                 StringBuilder b = new StringBuilder(256);
2480                 b.append("beginBackup: [");
2481                 for (BackupRequest req : mOriginalQueue) {
2482                     b.append(' ');
2483                     b.append(req.packageName);
2484                 }
2485                 b.append(" ]");
2486                 addBackupTrace(b.toString());
2487             }
2488 
2489             mAgentBinder = null;
2490             mStatus = BackupTransport.TRANSPORT_OK;
2491 
2492             // Sanity check: if the queue is empty we have no work to do.
2493             if (mOriginalQueue.isEmpty()) {
2494                 Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
2495                 addBackupTrace("queue empty at begin");
2496                 executeNextState(BackupState.FINAL);
2497                 return;
2498             }
2499 
2500             // We need to retain the original queue contents in case of transport
2501             // failure, but we want a working copy that we can manipulate along
2502             // the way.
2503             mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
2504 
2505             // The app metadata pseudopackage might also be represented in the
2506             // backup queue if apps have been added/removed since the last time
2507             // we performed a backup.  Drop it from the working queue now that
2508             // we're committed to evaluating it for backup regardless.
2509             for (int i = 0; i < mQueue.size(); i++) {
2510                 if (PACKAGE_MANAGER_SENTINEL.equals(mQueue.get(i).packageName)) {
2511                     if (MORE_DEBUG) {
2512                         Slog.i(TAG, "Metadata in queue; eliding");
2513                     }
2514                     mQueue.remove(i);
2515                     break;
2516                 }
2517             }
2518 
2519             if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
2520 
2521             File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2522             try {
2523                 final String transportName = mTransport.transportDirName();
2524                 EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
2525 
2526                 // If we haven't stored package manager metadata yet, we must init the transport.
2527                 if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
2528                     Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
2529                     addBackupTrace("initializing transport " + transportName);
2530                     resetBackupState(mStateDir);  // Just to make sure.
2531                     mStatus = mTransport.initializeDevice();
2532 
2533                     addBackupTrace("transport.initializeDevice() == " + mStatus);
2534                     if (mStatus == BackupTransport.TRANSPORT_OK) {
2535                         EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
2536                     } else {
2537                         EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
2538                         Slog.e(TAG, "Transport error in initializeDevice()");
2539                     }
2540                 }
2541 
2542                 // The package manager doesn't have a proper <application> etc, but since
2543                 // it's running here in the system process we can just set up its agent
2544                 // directly and use a synthetic BackupRequest.  We always run this pass
2545                 // because it's cheap and this way we guarantee that we don't get out of
2546                 // step even if we're selecting among various transports at run time.
2547                 if (mStatus == BackupTransport.TRANSPORT_OK) {
2548                     PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
2549                             mPackageManager);
2550                     mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
2551                             IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
2552                     addBackupTrace("PMBA invoke: " + mStatus);
2553 
2554                     // Because the PMBA is a local instance, it has already executed its
2555                     // backup callback and returned.  Blow away the lingering (spurious)
2556                     // pending timeout message for it.
2557                     mBackupHandler.removeMessages(MSG_TIMEOUT);
2558                 }
2559 
2560                 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
2561                     // The backend reports that our dataset has been wiped.  Note this in
2562                     // the event log; the no-success code below will reset the backup
2563                     // state as well.
2564                     EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
2565                 }
2566             } catch (Exception e) {
2567                 Slog.e(TAG, "Error in backup thread", e);
2568                 addBackupTrace("Exception in backup thread: " + e);
2569                 mStatus = BackupTransport.TRANSPORT_ERROR;
2570             } finally {
2571                 // If we've succeeded so far, invokeAgentForBackup() will have run the PM
2572                 // metadata and its completion/timeout callback will continue the state
2573                 // machine chain.  If it failed that won't happen; we handle that now.
2574                 addBackupTrace("exiting prelim: " + mStatus);
2575                 if (mStatus != BackupTransport.TRANSPORT_OK) {
2576                     // if things went wrong at this point, we need to
2577                     // restage everything and try again later.
2578                     resetBackupState(mStateDir);  // Just to make sure.
2579                     executeNextState(BackupState.FINAL);
2580                 }
2581             }
2582         }
2583 
2584         // Transport has been initialized and the PM metadata submitted successfully
2585         // if that was warranted.  Now we process the single next thing in the queue.
invokeNextAgent()2586         void invokeNextAgent() {
2587             mStatus = BackupTransport.TRANSPORT_OK;
2588             addBackupTrace("invoke q=" + mQueue.size());
2589 
2590             // Sanity check that we have work to do.  If not, skip to the end where
2591             // we reestablish the wakelock invariants etc.
2592             if (mQueue.isEmpty()) {
2593                 if (MORE_DEBUG) Slog.i(TAG, "queue now empty");
2594                 executeNextState(BackupState.FINAL);
2595                 return;
2596             }
2597 
2598             // pop the entry we're going to process on this step
2599             BackupRequest request = mQueue.get(0);
2600             mQueue.remove(0);
2601 
2602             Slog.d(TAG, "starting key/value backup of " + request);
2603             addBackupTrace("launch agent for " + request.packageName);
2604 
2605             // Verify that the requested app exists; it might be something that
2606             // requested a backup but was then uninstalled.  The request was
2607             // journalled and rather than tamper with the journal it's safer
2608             // to sanity-check here.  This also gives us the classname of the
2609             // package's backup agent.
2610             try {
2611                 mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
2612                         PackageManager.GET_SIGNATURES);
2613                 if (!appIsEligibleForBackup(mCurrentPackage.applicationInfo)) {
2614                     // The manifest has changed but we had a stale backup request pending.
2615                     // This won't happen again because the app won't be requesting further
2616                     // backups.
2617                     Slog.i(TAG, "Package " + request.packageName
2618                             + " no longer supports backup; skipping");
2619                     addBackupTrace("skipping - not eligible, completion is noop");
2620                     executeNextState(BackupState.RUNNING_QUEUE);
2621                     return;
2622                 }
2623 
2624                 if (appGetsFullBackup(mCurrentPackage)) {
2625                     // It's possible that this app *formerly* was enqueued for key/value backup,
2626                     // but has since been updated and now only supports the full-data path.
2627                     // Don't proceed with a key/value backup for it in this case.
2628                     Slog.i(TAG, "Package " + request.packageName
2629                             + " requests full-data rather than key/value; skipping");
2630                     addBackupTrace("skipping - fullBackupOnly, completion is noop");
2631                     executeNextState(BackupState.RUNNING_QUEUE);
2632                     return;
2633                 }
2634 
2635                 if ((mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
2636                     // The app has been force-stopped or cleared or just installed,
2637                     // and not yet launched out of that state, so just as it won't
2638                     // receive broadcasts, we won't run it for backup.
2639                     addBackupTrace("skipping - stopped");
2640                     executeNextState(BackupState.RUNNING_QUEUE);
2641                     return;
2642                 }
2643 
2644                 IBackupAgent agent = null;
2645                 try {
2646                     mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
2647                     agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
2648                             IApplicationThread.BACKUP_MODE_INCREMENTAL);
2649                     addBackupTrace("agent bound; a? = " + (agent != null));
2650                     if (agent != null) {
2651                         mAgentBinder = agent;
2652                         mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
2653                         // at this point we'll either get a completion callback from the
2654                         // agent, or a timeout message on the main handler.  either way, we're
2655                         // done here as long as we're successful so far.
2656                     } else {
2657                         // Timeout waiting for the agent
2658                         mStatus = BackupTransport.AGENT_ERROR;
2659                     }
2660                 } catch (SecurityException ex) {
2661                     // Try for the next one.
2662                     Slog.d(TAG, "error in bind/backup", ex);
2663                     mStatus = BackupTransport.AGENT_ERROR;
2664                             addBackupTrace("agent SE");
2665                 }
2666             } catch (NameNotFoundException e) {
2667                 Slog.d(TAG, "Package does not exist; skipping");
2668                 addBackupTrace("no such package");
2669                 mStatus = BackupTransport.AGENT_UNKNOWN;
2670             } finally {
2671                 mWakelock.setWorkSource(null);
2672 
2673                 // If there was an agent error, no timeout/completion handling will occur.
2674                 // That means we need to direct to the next state ourselves.
2675                 if (mStatus != BackupTransport.TRANSPORT_OK) {
2676                     BackupState nextState = BackupState.RUNNING_QUEUE;
2677                     mAgentBinder = null;
2678 
2679                     // An agent-level failure means we reenqueue this one agent for
2680                     // a later retry, but otherwise proceed normally.
2681                     if (mStatus == BackupTransport.AGENT_ERROR) {
2682                         if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
2683                                 + " - restaging");
2684                         dataChangedImpl(request.packageName);
2685                         mStatus = BackupTransport.TRANSPORT_OK;
2686                         if (mQueue.isEmpty()) nextState = BackupState.FINAL;
2687                     } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
2688                         // Failed lookup of the app, so we couldn't bring up an agent, but
2689                         // we're otherwise fine.  Just drop it and go on to the next as usual.
2690                         mStatus = BackupTransport.TRANSPORT_OK;
2691                     } else {
2692                         // Transport-level failure means we reenqueue everything
2693                         revertAndEndBackup();
2694                         nextState = BackupState.FINAL;
2695                     }
2696 
2697                     executeNextState(nextState);
2698                 } else {
2699                     // success case
2700                     addBackupTrace("expecting completion/timeout callback");
2701                 }
2702             }
2703         }
2704 
finalizeBackup()2705         void finalizeBackup() {
2706             addBackupTrace("finishing");
2707 
2708             // Either backup was successful, in which case we of course do not need
2709             // this pass's journal any more; or it failed, in which case we just
2710             // re-enqueued all of these packages in the current active journal.
2711             // Either way, we no longer need this pass's journal.
2712             if (mJournal != null && !mJournal.delete()) {
2713                 Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
2714             }
2715 
2716             // If everything actually went through and this is the first time we've
2717             // done a backup, we can now record what the current backup dataset token
2718             // is.
2719             if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) {
2720                 addBackupTrace("success; recording token");
2721                 try {
2722                     mCurrentToken = mTransport.getCurrentRestoreSet();
2723                     writeRestoreTokens();
2724                 } catch (RemoteException e) {
2725                     // nothing for it at this point, unfortunately, but this will be
2726                     // recorded the next time we fully succeed.
2727                     addBackupTrace("transport threw returning token");
2728                 }
2729             }
2730 
2731             // Set up the next backup pass - at this point we can set mBackupRunning
2732             // to false to allow another pass to fire, because we're done with the
2733             // state machine sequence and the wakelock is refcounted.
2734             synchronized (mQueueLock) {
2735                 mBackupRunning = false;
2736                 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
2737                     // Make sure we back up everything and perform the one-time init
2738                     clearMetadata();
2739                     if (MORE_DEBUG) Slog.d(TAG, "Server requires init; rerunning");
2740                     addBackupTrace("init required; rerunning");
2741                     backupNow();
2742                 }
2743             }
2744 
2745             // Only once we're entirely finished do we release the wakelock
2746             clearBackupTrace();
2747             Slog.i(BackupManagerService.TAG, "Backup pass finished.");
2748             mWakelock.release();
2749         }
2750 
2751         // Remove the PM metadata state. This will generate an init on the next pass.
clearMetadata()2752         void clearMetadata() {
2753             final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
2754             if (pmState.exists()) pmState.delete();
2755         }
2756 
2757         // Invoke an agent's doBackup() and start a timeout message spinning on the main
2758         // handler in case it doesn't get back to us.
invokeAgentForBackup(String packageName, IBackupAgent agent, IBackupTransport transport)2759         int invokeAgentForBackup(String packageName, IBackupAgent agent,
2760                 IBackupTransport transport) {
2761             if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
2762             addBackupTrace("invoking " + packageName);
2763 
2764             mSavedStateName = new File(mStateDir, packageName);
2765             mBackupDataName = new File(mDataDir, packageName + ".data");
2766             mNewStateName = new File(mStateDir, packageName + ".new");
2767             if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName);
2768 
2769             mSavedState = null;
2770             mBackupData = null;
2771             mNewState = null;
2772 
2773             final int token = generateToken();
2774             try {
2775                 // Look up the package info & signatures.  This is first so that if it
2776                 // throws an exception, there's no file setup yet that would need to
2777                 // be unraveled.
2778                 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
2779                     // The metadata 'package' is synthetic; construct one and make
2780                     // sure our global state is pointed at it
2781                     mCurrentPackage = new PackageInfo();
2782                     mCurrentPackage.packageName = packageName;
2783                 }
2784 
2785                 // In a full backup, we pass a null ParcelFileDescriptor as
2786                 // the saved-state "file". This is by definition an incremental,
2787                 // so we build a saved state file to pass.
2788                 mSavedState = ParcelFileDescriptor.open(mSavedStateName,
2789                         ParcelFileDescriptor.MODE_READ_ONLY |
2790                         ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
2791 
2792                 mBackupData = ParcelFileDescriptor.open(mBackupDataName,
2793                         ParcelFileDescriptor.MODE_READ_WRITE |
2794                         ParcelFileDescriptor.MODE_CREATE |
2795                         ParcelFileDescriptor.MODE_TRUNCATE);
2796 
2797                 if (!SELinux.restorecon(mBackupDataName)) {
2798                     Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
2799                 }
2800 
2801                 mNewState = ParcelFileDescriptor.open(mNewStateName,
2802                         ParcelFileDescriptor.MODE_READ_WRITE |
2803                         ParcelFileDescriptor.MODE_CREATE |
2804                         ParcelFileDescriptor.MODE_TRUNCATE);
2805 
2806                 // Initiate the target's backup pass
2807                 addBackupTrace("setting timeout");
2808                 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
2809                 addBackupTrace("calling agent doBackup()");
2810                 agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
2811             } catch (Exception e) {
2812                 Slog.e(TAG, "Error invoking for backup on " + packageName);
2813                 addBackupTrace("exception: " + e);
2814                 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
2815                         e.toString());
2816                 agentErrorCleanup();
2817                 return BackupTransport.AGENT_ERROR;
2818             }
2819 
2820             // At this point the agent is off and running.  The next thing to happen will
2821             // either be a callback from the agent, at which point we'll process its data
2822             // for transport, or a timeout.  Either way the next phase will happen in
2823             // response to the TimeoutHandler interface callbacks.
2824             addBackupTrace("invoke success");
2825             return BackupTransport.TRANSPORT_OK;
2826         }
2827 
failAgent(IBackupAgent agent, String message)2828         public void failAgent(IBackupAgent agent, String message) {
2829             try {
2830                 agent.fail(message);
2831             } catch (Exception e) {
2832                 Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName);
2833             }
2834         }
2835 
2836         // SHA-1 a byte array and return the result in hex
SHA1Checksum(byte[] input)2837         private String SHA1Checksum(byte[] input) {
2838             final byte[] checksum;
2839             try {
2840                 MessageDigest md = MessageDigest.getInstance("SHA-1");
2841                 checksum = md.digest(input);
2842             } catch (NoSuchAlgorithmException e) {
2843                 Slog.e(TAG, "Unable to use SHA-1!");
2844                 return "00";
2845             }
2846 
2847             StringBuffer sb = new StringBuffer(checksum.length * 2);
2848             for (int i = 0; i < checksum.length; i++) {
2849                 sb.append(Integer.toHexString(checksum[i]));
2850             }
2851             return sb.toString();
2852         }
2853 
writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)2854         private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
2855                 throws IOException {
2856             byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
2857                     UserHandle.USER_OWNER);
2858             // has the widget state changed since last time?
2859             final File widgetFile = new File(mStateDir, pkgName + "_widget");
2860             final boolean priorStateExists = widgetFile.exists();
2861 
2862             if (MORE_DEBUG) {
2863                 if (priorStateExists || widgetState != null) {
2864                     Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
2865                             + " prior=" + priorStateExists);
2866                 }
2867             }
2868 
2869             if (!priorStateExists && widgetState == null) {
2870                 // no prior state, no new state => nothing to do
2871                 return;
2872             }
2873 
2874             // if the new state is not null, we might need to compare checksums to
2875             // determine whether to update the widget blob in the archive.  If the
2876             // widget state *is* null, we know a priori at this point that we simply
2877             // need to commit a deletion for it.
2878             String newChecksum = null;
2879             if (widgetState != null) {
2880                 newChecksum = SHA1Checksum(widgetState);
2881                 if (priorStateExists) {
2882                     final String priorChecksum;
2883                     try (
2884                         FileInputStream fin = new FileInputStream(widgetFile);
2885                         DataInputStream in = new DataInputStream(fin)
2886                     ) {
2887                         priorChecksum = in.readUTF();
2888                     }
2889                     if (Objects.equals(newChecksum, priorChecksum)) {
2890                         // Same checksum => no state change => don't rewrite the widget data
2891                         return;
2892                     }
2893                 }
2894             } // else widget state *became* empty, so we need to commit a deletion
2895 
2896             BackupDataOutput out = new BackupDataOutput(fd);
2897             if (widgetState != null) {
2898                 try (
2899                     FileOutputStream fout = new FileOutputStream(widgetFile);
2900                     DataOutputStream stateOut = new DataOutputStream(fout)
2901                 ) {
2902                     stateOut.writeUTF(newChecksum);
2903                 }
2904 
2905                 out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
2906                 out.writeEntityData(widgetState, widgetState.length);
2907             } else {
2908                 // Widget state for this app has been removed; commit a deletion
2909                 out.writeEntityHeader(KEY_WIDGET_STATE, -1);
2910                 widgetFile.delete();
2911             }
2912         }
2913 
2914         @Override
operationComplete(int unusedResult)2915         public void operationComplete(int unusedResult) {
2916             // The agent reported back to us!
2917 
2918             if (mBackupData == null) {
2919                 // This callback was racing with our timeout, so we've cleaned up the
2920                 // agent state already and are on to the next thing.  We have nothing
2921                 // further to do here: agent state having been cleared means that we've
2922                 // initiated the appropriate next operation.
2923                 final String pkg = (mCurrentPackage != null)
2924                         ? mCurrentPackage.packageName : "[none]";
2925                 if (MORE_DEBUG) {
2926                     Slog.i(TAG, "Callback after agent teardown: " + pkg);
2927                 }
2928                 addBackupTrace("late opComplete; curPkg = " + pkg);
2929                 return;
2930             }
2931 
2932             final String pkgName = mCurrentPackage.packageName;
2933             final long filepos = mBackupDataName.length();
2934             FileDescriptor fd = mBackupData.getFileDescriptor();
2935             try {
2936                 // If it's a 3rd party app, see whether they wrote any protected keys
2937                 // and complain mightily if they are attempting shenanigans.
2938                 if (mCurrentPackage.applicationInfo != null &&
2939                         (mCurrentPackage.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
2940                     ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
2941                             ParcelFileDescriptor.MODE_READ_ONLY);
2942                     BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
2943                     try {
2944                         while (in.readNextHeader()) {
2945                             final String key = in.getKey();
2946                             if (key != null && key.charAt(0) >= 0xff00) {
2947                                 // Not okay: crash them and bail.
2948                                 failAgent(mAgentBinder, "Illegal backup key: " + key);
2949                                 addBackupTrace("illegal key " + key + " from " + pkgName);
2950                                 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
2951                                         "bad key");
2952                                 mBackupHandler.removeMessages(MSG_TIMEOUT);
2953                                 agentErrorCleanup();
2954                                 // agentErrorCleanup() implicitly executes next state properly
2955                                 return;
2956                             }
2957                             in.skipEntityData();
2958                         }
2959                     } finally {
2960                         if (readFd != null) {
2961                             readFd.close();
2962                         }
2963                     }
2964                 }
2965 
2966                 // Piggyback the widget state payload, if any
2967                 writeWidgetPayloadIfAppropriate(fd, pkgName);
2968             } catch (IOException e) {
2969                 // Hard disk error; recovery/failure policy TBD.  For now roll back,
2970                 // but we may want to consider this a transport-level failure (i.e.
2971                 // we're in such a bad state that we can't contemplate doing backup
2972                 // operations any more during this pass).
2973                 Slog.w(TAG, "Unable to save widget state for " + pkgName);
2974                 try {
2975                     Os.ftruncate(fd, filepos);
2976                 } catch (ErrnoException ee) {
2977                     Slog.w(TAG, "Unable to roll back!");
2978                 }
2979             }
2980 
2981             // Spin the data off to the transport and proceed with the next stage.
2982             if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
2983                     + pkgName);
2984             mBackupHandler.removeMessages(MSG_TIMEOUT);
2985             clearAgentState();
2986             addBackupTrace("operation complete");
2987 
2988             ParcelFileDescriptor backupData = null;
2989             mStatus = BackupTransport.TRANSPORT_OK;
2990             try {
2991                 int size = (int) mBackupDataName.length();
2992                 if (size > 0) {
2993                     if (mStatus == BackupTransport.TRANSPORT_OK) {
2994                         backupData = ParcelFileDescriptor.open(mBackupDataName,
2995                                 ParcelFileDescriptor.MODE_READ_ONLY);
2996                         addBackupTrace("sending data to transport");
2997                         mStatus = mTransport.performBackup(mCurrentPackage, backupData);
2998                     }
2999 
3000                     // TODO - We call finishBackup() for each application backed up, because
3001                     // we need to know now whether it succeeded or failed.  Instead, we should
3002                     // hold off on finishBackup() until the end, which implies holding off on
3003                     // renaming *all* the output state files (see below) until that happens.
3004 
3005                     addBackupTrace("data delivered: " + mStatus);
3006                     if (mStatus == BackupTransport.TRANSPORT_OK) {
3007                         addBackupTrace("finishing op on transport");
3008                         mStatus = mTransport.finishBackup();
3009                         addBackupTrace("finished: " + mStatus);
3010                     } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3011                         addBackupTrace("transport rejected package");
3012                     }
3013                 } else {
3014                     if (MORE_DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
3015                     addBackupTrace("no data to send");
3016                 }
3017 
3018                 if (mStatus == BackupTransport.TRANSPORT_OK) {
3019                     // After successful transport, delete the now-stale data
3020                     // and juggle the files so that next time we supply the agent
3021                     // with the new state file it just created.
3022                     mBackupDataName.delete();
3023                     mNewStateName.renameTo(mSavedStateName);
3024                     EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
3025                     logBackupComplete(pkgName);
3026                 } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3027                     // The transport has rejected backup of this specific package.  Roll it
3028                     // back but proceed with running the rest of the queue.
3029                     mBackupDataName.delete();
3030                     mNewStateName.delete();
3031                     EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
3032                 } else {
3033                     // Actual transport-level failure to communicate the data to the backend
3034                     EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
3035                 }
3036             } catch (Exception e) {
3037                 Slog.e(TAG, "Transport error backing up " + pkgName, e);
3038                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
3039                 mStatus = BackupTransport.TRANSPORT_ERROR;
3040             } finally {
3041                 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
3042             }
3043 
3044             final BackupState nextState;
3045             if (mStatus == BackupTransport.TRANSPORT_OK
3046                     || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
3047                 // Success or single-package rejection.  Proceed with the next app if any,
3048                 // otherwise we're done.
3049                 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
3050             } else {
3051                 // Any other error here indicates a transport-level failure.  That means
3052                 // we need to halt everything and reschedule everything for next time.
3053                 revertAndEndBackup();
3054                 nextState = BackupState.FINAL;
3055             }
3056 
3057             executeNextState(nextState);
3058         }
3059 
3060         @Override
handleTimeout()3061         public void handleTimeout() {
3062             // Whoops, the current agent timed out running doBackup().  Tidy up and restage
3063             // it for the next time we run a backup pass.
3064             // !!! TODO: keep track of failure counts per agent, and blacklist those which
3065             // fail repeatedly (i.e. have proved themselves to be buggy).
3066             Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
3067             EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
3068                     "timeout");
3069             addBackupTrace("timeout of " + mCurrentPackage.packageName);
3070             agentErrorCleanup();
3071             dataChangedImpl(mCurrentPackage.packageName);
3072         }
3073 
revertAndEndBackup()3074         void revertAndEndBackup() {
3075             if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
3076             addBackupTrace("transport error; reverting");
3077 
3078             // We want to reset the backup schedule based on whatever the transport suggests
3079             // by way of retry/backoff time.
3080             long delay;
3081             try {
3082                 delay = mTransport.requestBackupTime();
3083             } catch (Exception e) {
3084                 Slog.w(TAG, "Unable to contact transport for recommended backoff");
3085                 delay = 0;  // use the scheduler's default
3086             }
3087             KeyValueBackupJob.schedule(mContext, delay);
3088 
3089             for (BackupRequest request : mOriginalQueue) {
3090                 dataChangedImpl(request.packageName);
3091             }
3092 
3093         }
3094 
agentErrorCleanup()3095         void agentErrorCleanup() {
3096             mBackupDataName.delete();
3097             mNewStateName.delete();
3098             clearAgentState();
3099 
3100             executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
3101         }
3102 
3103         // Cleanup common to both success and failure cases
clearAgentState()3104         void clearAgentState() {
3105             try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
3106             try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
3107             try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
3108             synchronized (mCurrentOpLock) {
3109                 // Current-operation callback handling requires the validity of these various
3110                 // bits of internal state as an invariant of the operation still being live.
3111                 // This means we make sure to clear all of the state in unison inside the lock.
3112                 mCurrentOperations.clear();
3113                 mSavedState = mBackupData = mNewState = null;
3114             }
3115 
3116             // If this was a pseudopackage there's no associated Activity Manager state
3117             if (mCurrentPackage.applicationInfo != null) {
3118                 addBackupTrace("unbinding " + mCurrentPackage.packageName);
3119                 try {  // unbind even on timeout, just in case
3120                     mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
3121                 } catch (RemoteException e) { /* can't happen; activity manager is local */ }
3122             }
3123         }
3124 
executeNextState(BackupState nextState)3125         void executeNextState(BackupState nextState) {
3126             if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
3127                     + this + " nextState=" + nextState);
3128             addBackupTrace("executeNextState => " + nextState);
3129             mCurrentState = nextState;
3130             Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
3131             mBackupHandler.sendMessage(msg);
3132         }
3133     }
3134 
3135 
3136     // ----- Full backup/restore to a file/socket -----
3137 
3138     class FullBackupObbConnection implements ServiceConnection {
3139         volatile IObbBackupService mService;
3140 
FullBackupObbConnection()3141         FullBackupObbConnection() {
3142             mService = null;
3143         }
3144 
establish()3145         public void establish() {
3146             if (MORE_DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
3147             Intent obbIntent = new Intent().setComponent(new ComponentName(
3148                     "com.android.sharedstoragebackup",
3149                     "com.android.sharedstoragebackup.ObbBackupService"));
3150             BackupManagerService.this.mContext.bindService(
3151                     obbIntent, this, Context.BIND_AUTO_CREATE);
3152         }
3153 
tearDown()3154         public void tearDown() {
3155             BackupManagerService.this.mContext.unbindService(this);
3156         }
3157 
backupObbs(PackageInfo pkg, OutputStream out)3158         public boolean backupObbs(PackageInfo pkg, OutputStream out) {
3159             boolean success = false;
3160             waitForConnection();
3161 
3162             ParcelFileDescriptor[] pipes = null;
3163             try {
3164                 pipes = ParcelFileDescriptor.createPipe();
3165                 int token = generateToken();
3166                 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3167                 mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
3168                 routeSocketDataToOutput(pipes[0], out);
3169                 success = waitUntilOperationComplete(token);
3170             } catch (Exception e) {
3171                 Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
3172             } finally {
3173                 try {
3174                     out.flush();
3175                     if (pipes != null) {
3176                         if (pipes[0] != null) pipes[0].close();
3177                         if (pipes[1] != null) pipes[1].close();
3178                     }
3179                 } catch (IOException e) {
3180                     Slog.w(TAG, "I/O error closing down OBB backup", e);
3181                 }
3182             }
3183             return success;
3184         }
3185 
restoreObbFile(String pkgName, ParcelFileDescriptor data, long fileSize, int type, String path, long mode, long mtime, int token, IBackupManager callbackBinder)3186         public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
3187                 long fileSize, int type, String path, long mode, long mtime,
3188                 int token, IBackupManager callbackBinder) {
3189             waitForConnection();
3190 
3191             try {
3192                 mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
3193                         token, callbackBinder);
3194             } catch (Exception e) {
3195                 Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
3196             }
3197         }
3198 
waitForConnection()3199         private void waitForConnection() {
3200             synchronized (this) {
3201                 while (mService == null) {
3202                     if (MORE_DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
3203                     try {
3204                         this.wait();
3205                     } catch (InterruptedException e) { /* never interrupted */ }
3206                 }
3207                 if (MORE_DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
3208             }
3209         }
3210 
3211         @Override
onServiceConnected(ComponentName name, IBinder service)3212         public void onServiceConnected(ComponentName name, IBinder service) {
3213             synchronized (this) {
3214                 mService = IObbBackupService.Stub.asInterface(service);
3215                 if (MORE_DEBUG) Slog.i(TAG, "OBB service connection " + mService
3216                         + " connected on " + this);
3217                 this.notifyAll();
3218             }
3219         }
3220 
3221         @Override
onServiceDisconnected(ComponentName name)3222         public void onServiceDisconnected(ComponentName name) {
3223             synchronized (this) {
3224                 mService = null;
3225                 if (MORE_DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
3226                 this.notifyAll();
3227             }
3228         }
3229 
3230     }
3231 
routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)3232     private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
3233             throws IOException {
3234         FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
3235         DataInputStream in = new DataInputStream(raw);
3236 
3237         byte[] buffer = new byte[32 * 1024];
3238         int chunkTotal;
3239         while ((chunkTotal = in.readInt()) > 0) {
3240             while (chunkTotal > 0) {
3241                 int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
3242                 int nRead = in.read(buffer, 0, toRead);
3243                 out.write(buffer, 0, nRead);
3244                 chunkTotal -= nRead;
3245             }
3246         }
3247     }
3248 
tearDownAgentAndKill(ApplicationInfo app)3249     void tearDownAgentAndKill(ApplicationInfo app) {
3250         try {
3251             // unbind and tidy up even on timeout or failure, just in case
3252             mActivityManager.unbindBackupAgent(app);
3253 
3254             // The agent was running with a stub Application object, so shut it down.
3255             // !!! We hardcode the confirmation UI's package name here rather than use a
3256             //     manifest flag!  TODO something less direct.
3257             if (app.uid != Process.SYSTEM_UID
3258                     && !app.packageName.equals("com.android.backupconfirm")
3259                     && app.uid != Process.PHONE_UID) {
3260                 if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
3261                 mActivityManager.killApplicationProcess(app.processName, app.uid);
3262             } else {
3263                 if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
3264             }
3265         } catch (RemoteException e) {
3266             Slog.d(TAG, "Lost app trying to shut down");
3267         }
3268     }
3269 
3270     // Core logic for performing one package's full backup, gathering the tarball from the
3271     // application and emitting it to the designated OutputStream.
3272 
3273     // Callout from the engine to an interested participant that might need to communicate
3274     // with the agent prior to asking it to move data
3275     interface FullBackupPreflight {
3276         /**
3277          * Perform the preflight operation necessary for the given package.
3278          * @param pkg The name of the package being proposed for full-data backup
3279          * @param agent Live BackupAgent binding to the target app's agent
3280          * @return BackupTransport.TRANSPORT_OK to proceed with the backup operation,
3281          *         or one of the other BackupTransport.* error codes as appropriate
3282          */
preflightFullBackup(PackageInfo pkg, IBackupAgent agent)3283         int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
3284     };
3285 
3286     class FullBackupEngine {
3287         OutputStream mOutput;
3288         FullBackupPreflight mPreflightHook;
3289         IFullBackupRestoreObserver mObserver;
3290         File mFilesDir;
3291         File mManifestFile;
3292         File mMetadataFile;
3293         boolean mIncludeApks;
3294 
3295         class FullBackupRunner implements Runnable {
3296             PackageInfo mPackage;
3297             byte[] mWidgetData;
3298             IBackupAgent mAgent;
3299             ParcelFileDescriptor mPipe;
3300             int mToken;
3301             boolean mSendApk;
3302             boolean mWriteManifest;
3303 
FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe, int token, boolean sendApk, boolean writeManifest, byte[] widgetData)3304             FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
3305                     int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
3306                             throws IOException {
3307                 mPackage = pack;
3308                 mWidgetData = widgetData;
3309                 mAgent = agent;
3310                 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
3311                 mToken = token;
3312                 mSendApk = sendApk;
3313                 mWriteManifest = writeManifest;
3314             }
3315 
3316             @Override
run()3317             public void run() {
3318                 try {
3319                     FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
3320 
3321                     if (mWriteManifest) {
3322                         final boolean writeWidgetData = mWidgetData != null;
3323                         if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
3324                         writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
3325                         FullBackup.backupToTar(mPackage.packageName, null, null,
3326                                 mFilesDir.getAbsolutePath(),
3327                                 mManifestFile.getAbsolutePath(),
3328                                 output);
3329                         mManifestFile.delete();
3330 
3331                         // We only need to write a metadata file if we have widget data to stash
3332                         if (writeWidgetData) {
3333                             writeMetadata(mPackage, mMetadataFile, mWidgetData);
3334                             FullBackup.backupToTar(mPackage.packageName, null, null,
3335                                     mFilesDir.getAbsolutePath(),
3336                                     mMetadataFile.getAbsolutePath(),
3337                                     output);
3338                             mMetadataFile.delete();
3339                         }
3340                     }
3341 
3342                     if (mSendApk) {
3343                         writeApkToBackup(mPackage, output);
3344                     }
3345 
3346                     if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
3347                     prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null);
3348                     mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
3349                 } catch (IOException e) {
3350                     Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
3351                 } catch (RemoteException e) {
3352                     Slog.e(TAG, "Remote agent vanished during full backup of "
3353                             + mPackage.packageName);
3354                 } finally {
3355                     try {
3356                         mPipe.close();
3357                     } catch (IOException e) {}
3358                 }
3359             }
3360         }
3361 
FullBackupEngine(OutputStream output, String packageName, FullBackupPreflight preflightHook, boolean alsoApks)3362         FullBackupEngine(OutputStream output, String packageName, FullBackupPreflight preflightHook,
3363                 boolean alsoApks) {
3364             mOutput = output;
3365             mPreflightHook = preflightHook;
3366             mIncludeApks = alsoApks;
3367             mFilesDir = new File("/data/system");
3368             mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
3369             mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
3370         }
3371 
backupOnePackage(PackageInfo pkg)3372         public int backupOnePackage(PackageInfo pkg) throws RemoteException {
3373             int result = BackupTransport.TRANSPORT_OK;
3374             Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
3375 
3376             IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
3377                     IApplicationThread.BACKUP_MODE_FULL);
3378             if (agent != null) {
3379                 ParcelFileDescriptor[] pipes = null;
3380                 try {
3381                     // Call the preflight hook, if any
3382                     if (mPreflightHook != null) {
3383                         result = mPreflightHook.preflightFullBackup(pkg, agent);
3384                         if (MORE_DEBUG) {
3385                             Slog.v(TAG, "preflight returned " + result);
3386                         }
3387                     }
3388 
3389                     // If we're still good to go after preflighting, start moving data
3390                     if (result == BackupTransport.TRANSPORT_OK) {
3391                         pipes = ParcelFileDescriptor.createPipe();
3392 
3393                         ApplicationInfo app = pkg.applicationInfo;
3394                         final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
3395                         final boolean sendApk = mIncludeApks
3396                                 && !isSharedStorage
3397                                 && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
3398                                 && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
3399                                 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
3400 
3401                         byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
3402                                 UserHandle.USER_OWNER);
3403 
3404                         final int token = generateToken();
3405                         FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
3406                                 token, sendApk, !isSharedStorage, widgetBlob);
3407                         pipes[1].close();   // the runner has dup'd it
3408                         pipes[1] = null;
3409                         Thread t = new Thread(runner, "app-data-runner");
3410                         t.start();
3411 
3412                         // Now pull data from the app and stuff it into the output
3413                         try {
3414                             routeSocketDataToOutput(pipes[0], mOutput);
3415                         } catch (IOException e) {
3416                             Slog.i(TAG, "Caught exception reading from agent", e);
3417                             result = BackupTransport.AGENT_ERROR;
3418                         }
3419 
3420                         if (!waitUntilOperationComplete(token)) {
3421                             Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
3422                             result = BackupTransport.AGENT_ERROR;
3423                         } else {
3424                             if (MORE_DEBUG) {
3425                                 Slog.d(TAG, "Full package backup success: " + pkg.packageName);
3426                             }
3427                         }
3428                     }
3429                 } catch (IOException e) {
3430                     Slog.e(TAG, "Error backing up " + pkg.packageName, e);
3431                     result = BackupTransport.AGENT_ERROR;
3432                 } finally {
3433                     try {
3434                         // flush after every package
3435                         mOutput.flush();
3436                         if (pipes != null) {
3437                             if (pipes[0] != null) pipes[0].close();
3438                             if (pipes[1] != null) pipes[1].close();
3439                         }
3440                     } catch (IOException e) {
3441                         Slog.w(TAG, "Error bringing down backup stack");
3442                         result = BackupTransport.TRANSPORT_ERROR;
3443                     }
3444                 }
3445             } else {
3446                 Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
3447                 result = BackupTransport.AGENT_ERROR;
3448             }
3449             tearDown(pkg);
3450             return result;
3451         }
3452 
writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output)3453         private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
3454             // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
3455             // TODO: handle backing up split APKs
3456             final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
3457             final String apkDir = new File(appSourceDir).getParent();
3458             FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
3459                     apkDir, appSourceDir, output);
3460 
3461             // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
3462             // doesn't have access to external storage.
3463 
3464             // Save associated .obb content if it exists and we did save the apk
3465             // check for .obb and save those too
3466             final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_OWNER);
3467             final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0];
3468             if (obbDir != null) {
3469                 if (MORE_DEBUG) Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
3470                 File[] obbFiles = obbDir.listFiles();
3471                 if (obbFiles != null) {
3472                     final String obbDirName = obbDir.getAbsolutePath();
3473                     for (File obb : obbFiles) {
3474                         FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
3475                                 obbDirName, obb.getAbsolutePath(), output);
3476                     }
3477                 }
3478             }
3479         }
3480 
writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk, boolean withWidgets)3481         private void writeAppManifest(PackageInfo pkg, File manifestFile,
3482                 boolean withApk, boolean withWidgets) throws IOException {
3483             // Manifest format. All data are strings ending in LF:
3484             //     BACKUP_MANIFEST_VERSION, currently 1
3485             //
3486             // Version 1:
3487             //     package name
3488             //     package's versionCode
3489             //     platform versionCode
3490             //     getInstallerPackageName() for this package (maybe empty)
3491             //     boolean: "1" if archive includes .apk; any other string means not
3492             //     number of signatures == N
3493             // N*:    signature byte array in ascii format per Signature.toCharsString()
3494             StringBuilder builder = new StringBuilder(4096);
3495             StringBuilderPrinter printer = new StringBuilderPrinter(builder);
3496 
3497             printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
3498             printer.println(pkg.packageName);
3499             printer.println(Integer.toString(pkg.versionCode));
3500             printer.println(Integer.toString(Build.VERSION.SDK_INT));
3501 
3502             String installerName = mPackageManager.getInstallerPackageName(pkg.packageName);
3503             printer.println((installerName != null) ? installerName : "");
3504 
3505             printer.println(withApk ? "1" : "0");
3506             if (pkg.signatures == null) {
3507                 printer.println("0");
3508             } else {
3509                 printer.println(Integer.toString(pkg.signatures.length));
3510                 for (Signature sig : pkg.signatures) {
3511                     printer.println(sig.toCharsString());
3512                 }
3513             }
3514 
3515             FileOutputStream outstream = new FileOutputStream(manifestFile);
3516             outstream.write(builder.toString().getBytes());
3517             outstream.close();
3518 
3519             // We want the manifest block in the archive stream to be idempotent:
3520             // each time we generate a backup stream for the app, we want the manifest
3521             // block to be identical.  The underlying tar mechanism sees it as a file,
3522             // though, and will propagate its mtime, causing the tar header to vary.
3523             // Avoid this problem by pinning the mtime to zero.
3524             manifestFile.setLastModified(0);
3525         }
3526 
3527         // Widget metadata format. All header entries are strings ending in LF:
3528         //
3529         // Version 1 header:
3530         //     BACKUP_METADATA_VERSION, currently "1"
3531         //     package name
3532         //
3533         // File data (all integers are binary in network byte order)
3534         // *N: 4 : integer token identifying which metadata blob
3535         //     4 : integer size of this blob = N
3536         //     N : raw bytes of this metadata blob
3537         //
3538         // Currently understood blobs (always in network byte order):
3539         //
3540         //     widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
3541         //
3542         // Unrecognized blobs are *ignored*, not errors.
writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)3543         private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
3544                 throws IOException {
3545             StringBuilder b = new StringBuilder(512);
3546             StringBuilderPrinter printer = new StringBuilderPrinter(b);
3547             printer.println(Integer.toString(BACKUP_METADATA_VERSION));
3548             printer.println(pkg.packageName);
3549 
3550             FileOutputStream fout = new FileOutputStream(destination);
3551             BufferedOutputStream bout = new BufferedOutputStream(fout);
3552             DataOutputStream out = new DataOutputStream(bout);
3553             bout.write(b.toString().getBytes());    // bypassing DataOutputStream
3554 
3555             if (widgetData != null && widgetData.length > 0) {
3556                 out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
3557                 out.writeInt(widgetData.length);
3558                 out.write(widgetData);
3559             }
3560             bout.flush();
3561             out.close();
3562 
3563             // As with the manifest file, guarantee idempotence of the archive metadata
3564             // for the widget block by using a fixed mtime on the transient file.
3565             destination.setLastModified(0);
3566         }
3567 
tearDown(PackageInfo pkg)3568         private void tearDown(PackageInfo pkg) {
3569             if (pkg != null) {
3570                 final ApplicationInfo app = pkg.applicationInfo;
3571                 if (app != null) {
3572                     tearDownAgentAndKill(app);
3573                 }
3574             }
3575         }
3576     }
3577 
3578     // Generic driver skeleton for full backup operations
3579     abstract class FullBackupTask implements Runnable {
3580         IFullBackupRestoreObserver mObserver;
3581 
FullBackupTask(IFullBackupRestoreObserver observer)3582         FullBackupTask(IFullBackupRestoreObserver observer) {
3583             mObserver = observer;
3584         }
3585 
3586         // wrappers for observer use
sendStartBackup()3587         final void sendStartBackup() {
3588             if (mObserver != null) {
3589                 try {
3590                     mObserver.onStartBackup();
3591                 } catch (RemoteException e) {
3592                     Slog.w(TAG, "full backup observer went away: startBackup");
3593                     mObserver = null;
3594                 }
3595             }
3596         }
3597 
sendOnBackupPackage(String name)3598         final void sendOnBackupPackage(String name) {
3599             if (mObserver != null) {
3600                 try {
3601                     // TODO: use a more user-friendly name string
3602                     mObserver.onBackupPackage(name);
3603                 } catch (RemoteException e) {
3604                     Slog.w(TAG, "full backup observer went away: backupPackage");
3605                     mObserver = null;
3606                 }
3607             }
3608         }
3609 
sendEndBackup()3610         final void sendEndBackup() {
3611             if (mObserver != null) {
3612                 try {
3613                     mObserver.onEndBackup();
3614                 } catch (RemoteException e) {
3615                     Slog.w(TAG, "full backup observer went away: endBackup");
3616                     mObserver = null;
3617                 }
3618             }
3619         }
3620     }
3621 
deviceIsEncrypted()3622     boolean deviceIsEncrypted() {
3623         try {
3624             return mMountService.getEncryptionState()
3625                      != IMountService.ENCRYPTION_STATE_NONE
3626                 && mMountService.getPasswordType()
3627                      != StorageManager.CRYPT_TYPE_DEFAULT;
3628         } catch (Exception e) {
3629             // If we can't talk to the mount service we have a serious problem; fail
3630             // "secure" i.e. assuming that the device is encrypted.
3631             Slog.e(TAG, "Unable to communicate with mount service: " + e.getMessage());
3632             return true;
3633         }
3634     }
3635 
3636     // Full backup task variant used for adb backup
3637     class PerformAdbBackupTask extends FullBackupTask {
3638         FullBackupEngine mBackupEngine;
3639         final AtomicBoolean mLatch;
3640 
3641         ParcelFileDescriptor mOutputFile;
3642         DeflaterOutputStream mDeflater;
3643         boolean mIncludeApks;
3644         boolean mIncludeObbs;
3645         boolean mIncludeShared;
3646         boolean mDoWidgets;
3647         boolean mAllApps;
3648         boolean mIncludeSystem;
3649         boolean mCompress;
3650         ArrayList<String> mPackages;
3651         String mCurrentPassword;
3652         String mEncryptPassword;
3653 
PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch)3654         PerformAdbBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
3655                 boolean includeApks, boolean includeObbs, boolean includeShared,
3656                 boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
3657                 boolean doSystem, boolean doCompress, String[] packages, AtomicBoolean latch) {
3658             super(observer);
3659             mLatch = latch;
3660 
3661             mOutputFile = fd;
3662             mIncludeApks = includeApks;
3663             mIncludeObbs = includeObbs;
3664             mIncludeShared = includeShared;
3665             mDoWidgets = doWidgets;
3666             mAllApps = doAllApps;
3667             mIncludeSystem = doSystem;
3668             mPackages = (packages == null)
3669                     ? new ArrayList<String>()
3670                     : new ArrayList<String>(Arrays.asList(packages));
3671             mCurrentPassword = curPassword;
3672             // when backing up, if there is a current backup password, we require that
3673             // the user use a nonempty encryption password as well.  if one is supplied
3674             // in the UI we use that, but if the UI was left empty we fall back to the
3675             // current backup password (which was supplied by the user as well).
3676             if (encryptPassword == null || "".equals(encryptPassword)) {
3677                 mEncryptPassword = curPassword;
3678             } else {
3679                 mEncryptPassword = encryptPassword;
3680             }
3681             mCompress = doCompress;
3682         }
3683 
addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames)3684         void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
3685             for (String pkgName : pkgNames) {
3686                 if (!set.containsKey(pkgName)) {
3687                     try {
3688                         PackageInfo info = mPackageManager.getPackageInfo(pkgName,
3689                                 PackageManager.GET_SIGNATURES);
3690                         set.put(pkgName, info);
3691                     } catch (NameNotFoundException e) {
3692                         Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
3693                     }
3694                 }
3695             }
3696         }
3697 
emitAesBackupHeader(StringBuilder headerbuf, OutputStream ofstream)3698         private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
3699                 OutputStream ofstream) throws Exception {
3700             // User key will be used to encrypt the master key.
3701             byte[] newUserSalt = randomBytes(PBKDF2_SALT_SIZE);
3702             SecretKey userKey = buildPasswordKey(PBKDF_CURRENT, mEncryptPassword, newUserSalt,
3703                     PBKDF2_HASH_ROUNDS);
3704 
3705             // the master key is random for each backup
3706             byte[] masterPw = new byte[256 / 8];
3707             mRng.nextBytes(masterPw);
3708             byte[] checksumSalt = randomBytes(PBKDF2_SALT_SIZE);
3709 
3710             // primary encryption of the datastream with the random key
3711             Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
3712             SecretKeySpec masterKeySpec = new SecretKeySpec(masterPw, "AES");
3713             c.init(Cipher.ENCRYPT_MODE, masterKeySpec);
3714             OutputStream finalOutput = new CipherOutputStream(ofstream, c);
3715 
3716             // line 4: name of encryption algorithm
3717             headerbuf.append(ENCRYPTION_ALGORITHM_NAME);
3718             headerbuf.append('\n');
3719             // line 5: user password salt [hex]
3720             headerbuf.append(byteArrayToHex(newUserSalt));
3721             headerbuf.append('\n');
3722             // line 6: master key checksum salt [hex]
3723             headerbuf.append(byteArrayToHex(checksumSalt));
3724             headerbuf.append('\n');
3725             // line 7: number of PBKDF2 rounds used [decimal]
3726             headerbuf.append(PBKDF2_HASH_ROUNDS);
3727             headerbuf.append('\n');
3728 
3729             // line 8: IV of the user key [hex]
3730             Cipher mkC = Cipher.getInstance("AES/CBC/PKCS5Padding");
3731             mkC.init(Cipher.ENCRYPT_MODE, userKey);
3732 
3733             byte[] IV = mkC.getIV();
3734             headerbuf.append(byteArrayToHex(IV));
3735             headerbuf.append('\n');
3736 
3737             // line 9: master IV + key blob, encrypted by the user key [hex].  Blob format:
3738             //    [byte] IV length = Niv
3739             //    [array of Niv bytes] IV itself
3740             //    [byte] master key length = Nmk
3741             //    [array of Nmk bytes] master key itself
3742             //    [byte] MK checksum hash length = Nck
3743             //    [array of Nck bytes] master key checksum hash
3744             //
3745             // The checksum is the (master key + checksum salt), run through the
3746             // stated number of PBKDF2 rounds
3747             IV = c.getIV();
3748             byte[] mk = masterKeySpec.getEncoded();
3749             byte[] checksum = makeKeyChecksum(PBKDF_CURRENT, masterKeySpec.getEncoded(),
3750                     checksumSalt, PBKDF2_HASH_ROUNDS);
3751 
3752             ByteArrayOutputStream blob = new ByteArrayOutputStream(IV.length + mk.length
3753                     + checksum.length + 3);
3754             DataOutputStream mkOut = new DataOutputStream(blob);
3755             mkOut.writeByte(IV.length);
3756             mkOut.write(IV);
3757             mkOut.writeByte(mk.length);
3758             mkOut.write(mk);
3759             mkOut.writeByte(checksum.length);
3760             mkOut.write(checksum);
3761             mkOut.flush();
3762             byte[] encryptedMk = mkC.doFinal(blob.toByteArray());
3763             headerbuf.append(byteArrayToHex(encryptedMk));
3764             headerbuf.append('\n');
3765 
3766             return finalOutput;
3767         }
3768 
finalizeBackup(OutputStream out)3769         private void finalizeBackup(OutputStream out) {
3770             try {
3771                 // A standard 'tar' EOF sequence: two 512-byte blocks of all zeroes.
3772                 byte[] eof = new byte[512 * 2]; // newly allocated == zero filled
3773                 out.write(eof);
3774             } catch (IOException e) {
3775                 Slog.w(TAG, "Error attempting to finalize backup stream");
3776             }
3777         }
3778 
3779         @Override
run()3780         public void run() {
3781             Slog.i(TAG, "--- Performing full-dataset adb backup ---");
3782 
3783             TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
3784             FullBackupObbConnection obbConnection = new FullBackupObbConnection();
3785             obbConnection.establish();  // we'll want this later
3786 
3787             sendStartBackup();
3788 
3789             // doAllApps supersedes the package set if any
3790             if (mAllApps) {
3791                 List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
3792                         PackageManager.GET_SIGNATURES);
3793                 for (int i = 0; i < allPackages.size(); i++) {
3794                     PackageInfo pkg = allPackages.get(i);
3795                     // Exclude system apps if we've been asked to do so
3796                     if (mIncludeSystem == true
3797                             || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
3798                         packagesToBackup.put(pkg.packageName, pkg);
3799                     }
3800                 }
3801             }
3802 
3803             // If we're doing widget state as well, ensure that we have all the involved
3804             // host & provider packages in the set
3805             if (mDoWidgets) {
3806                 List<String> pkgs =
3807                         AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_OWNER);
3808                 if (pkgs != null) {
3809                     if (MORE_DEBUG) {
3810                         Slog.i(TAG, "Adding widget participants to backup set:");
3811                         StringBuilder sb = new StringBuilder(128);
3812                         sb.append("   ");
3813                         for (String s : pkgs) {
3814                             sb.append(' ');
3815                             sb.append(s);
3816                         }
3817                         Slog.i(TAG, sb.toString());
3818                     }
3819                     addPackagesToSet(packagesToBackup, pkgs);
3820                 }
3821             }
3822 
3823             // Now process the command line argument packages, if any. Note that explicitly-
3824             // named system-partition packages will be included even if includeSystem was
3825             // set to false.
3826             if (mPackages != null) {
3827                 addPackagesToSet(packagesToBackup, mPackages);
3828             }
3829 
3830             // Now we cull any inapplicable / inappropriate packages from the set.  This
3831             // includes the special shared-storage agent package; we handle that one
3832             // explicitly at the end of the backup pass.
3833             Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
3834             while (iter.hasNext()) {
3835                 PackageInfo pkg = iter.next().getValue();
3836                 if (!appIsEligibleForBackup(pkg.applicationInfo)) {
3837                     iter.remove();
3838                 }
3839             }
3840 
3841             // flatten the set of packages now so we can explicitly control the ordering
3842             ArrayList<PackageInfo> backupQueue =
3843                     new ArrayList<PackageInfo>(packagesToBackup.values());
3844             FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
3845             OutputStream out = null;
3846 
3847             PackageInfo pkg = null;
3848             try {
3849                 boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
3850 
3851                 // Only allow encrypted backups of encrypted devices
3852                 if (deviceIsEncrypted() && !encrypting) {
3853                     Slog.e(TAG, "Unencrypted backup of encrypted device; aborting");
3854                     return;
3855                 }
3856 
3857                 OutputStream finalOutput = ofstream;
3858 
3859                 // Verify that the given password matches the currently-active
3860                 // backup password, if any
3861                 if (!backupPasswordMatches(mCurrentPassword)) {
3862                     if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
3863                     return;
3864                 }
3865 
3866                 // Write the global file header.  All strings are UTF-8 encoded; lines end
3867                 // with a '\n' byte.  Actual backup data begins immediately following the
3868                 // final '\n'.
3869                 //
3870                 // line 1: "ANDROID BACKUP"
3871                 // line 2: backup file format version, currently "2"
3872                 // line 3: compressed?  "0" if not compressed, "1" if compressed.
3873                 // line 4: name of encryption algorithm [currently only "none" or "AES-256"]
3874                 //
3875                 // When line 4 is not "none", then additional header data follows:
3876                 //
3877                 // line 5: user password salt [hex]
3878                 // line 6: master key checksum salt [hex]
3879                 // line 7: number of PBKDF2 rounds to use (same for user & master) [decimal]
3880                 // line 8: IV of the user key [hex]
3881                 // line 9: master key blob [hex]
3882                 //     IV of the master key, master key itself, master key checksum hash
3883                 //
3884                 // The master key checksum is the master key plus its checksum salt, run through
3885                 // 10k rounds of PBKDF2.  This is used to verify that the user has supplied the
3886                 // correct password for decrypting the archive:  the master key decrypted from
3887                 // the archive using the user-supplied password is also run through PBKDF2 in
3888                 // this way, and if the result does not match the checksum as stored in the
3889                 // archive, then we know that the user-supplied password does not match the
3890                 // archive's.
3891                 StringBuilder headerbuf = new StringBuilder(1024);
3892 
3893                 headerbuf.append(BACKUP_FILE_HEADER_MAGIC);
3894                 headerbuf.append(BACKUP_FILE_VERSION); // integer, no trailing \n
3895                 headerbuf.append(mCompress ? "\n1\n" : "\n0\n");
3896 
3897                 try {
3898                     // Set up the encryption stage if appropriate, and emit the correct header
3899                     if (encrypting) {
3900                         finalOutput = emitAesBackupHeader(headerbuf, finalOutput);
3901                     } else {
3902                         headerbuf.append("none\n");
3903                     }
3904 
3905                     byte[] header = headerbuf.toString().getBytes("UTF-8");
3906                     ofstream.write(header);
3907 
3908                     // Set up the compression stage feeding into the encryption stage (if any)
3909                     if (mCompress) {
3910                         Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
3911                         finalOutput = new DeflaterOutputStream(finalOutput, deflater, true);
3912                     }
3913 
3914                     out = finalOutput;
3915                 } catch (Exception e) {
3916                     // Should never happen!
3917                     Slog.e(TAG, "Unable to emit archive header", e);
3918                     return;
3919                 }
3920 
3921                 // Shared storage if requested
3922                 if (mIncludeShared) {
3923                     try {
3924                         pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
3925                         backupQueue.add(pkg);
3926                     } catch (NameNotFoundException e) {
3927                         Slog.e(TAG, "Unable to find shared-storage backup handler");
3928                     }
3929                 }
3930 
3931                 // Now actually run the constructed backup sequence
3932                 int N = backupQueue.size();
3933                 for (int i = 0; i < N; i++) {
3934                     pkg = backupQueue.get(i);
3935                     final boolean isSharedStorage =
3936                             pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
3937 
3938                     mBackupEngine = new FullBackupEngine(out, pkg.packageName, null, mIncludeApks);
3939                     sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
3940                     mBackupEngine.backupOnePackage(pkg);
3941 
3942                     // after the app's agent runs to handle its private filesystem
3943                     // contents, back up any OBB content it has on its behalf.
3944                     if (mIncludeObbs) {
3945                         boolean obbOkay = obbConnection.backupObbs(pkg, out);
3946                         if (!obbOkay) {
3947                             throw new RuntimeException("Failure writing OBB stack for " + pkg);
3948                         }
3949                     }
3950                 }
3951 
3952                 // Done!
3953                 finalizeBackup(out);
3954             } catch (RemoteException e) {
3955                 Slog.e(TAG, "App died during full backup");
3956             } catch (Exception e) {
3957                 Slog.e(TAG, "Internal exception during full backup", e);
3958             } finally {
3959                 try {
3960                     if (out != null) out.close();
3961                     mOutputFile.close();
3962                 } catch (IOException e) {
3963                     /* nothing we can do about this */
3964                 }
3965                 synchronized (mCurrentOpLock) {
3966                     mCurrentOperations.clear();
3967                 }
3968                 synchronized (mLatch) {
3969                     mLatch.set(true);
3970                     mLatch.notifyAll();
3971                 }
3972                 sendEndBackup();
3973                 obbConnection.tearDown();
3974                 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
3975                 mWakelock.release();
3976             }
3977         }
3978     }
3979 
3980     // Full backup task extension used for transport-oriented operation
3981     class PerformFullTransportBackupTask extends FullBackupTask {
3982         static final String TAG = "PFTBT";
3983         ArrayList<PackageInfo> mPackages;
3984         boolean mUpdateSchedule;
3985         CountDownLatch mLatch;
3986         AtomicBoolean mKeepRunning;     // signal from job scheduler
3987         FullBackupJob mJob;             // if a scheduled job needs to be finished afterwards
3988 
PerformFullTransportBackupTask(IFullBackupRestoreObserver observer, String[] whichPackages, boolean updateSchedule, FullBackupJob runningJob, CountDownLatch latch)3989         PerformFullTransportBackupTask(IFullBackupRestoreObserver observer,
3990                 String[] whichPackages, boolean updateSchedule,
3991                 FullBackupJob runningJob, CountDownLatch latch) {
3992             super(observer);
3993             mUpdateSchedule = updateSchedule;
3994             mLatch = latch;
3995             mKeepRunning = new AtomicBoolean(true);
3996             mJob = runningJob;
3997             mPackages = new ArrayList<PackageInfo>(whichPackages.length);
3998 
3999             for (String pkg : whichPackages) {
4000                 try {
4001                     PackageInfo info = mPackageManager.getPackageInfo(pkg,
4002                             PackageManager.GET_SIGNATURES);
4003                     if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0
4004                             || pkg.equals(SHARED_BACKUP_AGENT_PACKAGE)) {
4005                         // Cull any packages that have indicated that backups are not permitted,
4006                         // as well as any explicit mention of the 'special' shared-storage agent
4007                         // package (we handle that one at the end).
4008                         if (MORE_DEBUG) {
4009                             Slog.d(TAG, "Ignoring opted-out package " + pkg);
4010                         }
4011                         continue;
4012                     } else if ((info.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
4013                             && (info.applicationInfo.backupAgentName == null)) {
4014                         // Cull any packages that run as system-domain uids but do not define their
4015                         // own backup agents
4016                         if (MORE_DEBUG) {
4017                             Slog.d(TAG, "Ignoring non-agent system package " + pkg);
4018                         }
4019                         continue;
4020                     } else if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
4021                         // Cull any packages in the 'stopped' state: they've either just been
4022                         // installed or have explicitly been force-stopped by the user.  In both
4023                         // cases we do not want to launch them for backup.
4024                         if (MORE_DEBUG) {
4025                             Slog.d(TAG, "Ignoring stopped package " + pkg);
4026                         }
4027                         continue;
4028                     }
4029                     mPackages.add(info);
4030                 } catch (NameNotFoundException e) {
4031                     Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
4032                 }
4033             }
4034         }
4035 
setRunning(boolean running)4036         public void setRunning(boolean running) {
4037             mKeepRunning.set(running);
4038         }
4039 
4040         @Override
run()4041         public void run() {
4042             // data from the app, passed to us for bridging to the transport
4043             ParcelFileDescriptor[] enginePipes = null;
4044 
4045             // Pipe through which we write data to the transport
4046             ParcelFileDescriptor[] transportPipes = null;
4047 
4048             PackageInfo currentPackage;
4049             long backoff = 0;
4050 
4051             try {
4052                 if (!mEnabled || !mProvisioned) {
4053                     // Backups are globally disabled, so don't proceed.
4054                     if (DEBUG) {
4055                         Slog.i(TAG, "full backup requested but e=" + mEnabled
4056                                 + " p=" + mProvisioned + "; ignoring");
4057                     }
4058                     mUpdateSchedule = false;
4059                     return;
4060                 }
4061 
4062                 IBackupTransport transport = getTransport(mCurrentTransport);
4063                 if (transport == null) {
4064                     Slog.w(TAG, "Transport not present; full data backup not performed");
4065                     return;
4066                 }
4067 
4068                 // Set up to send data to the transport
4069                 final int N = mPackages.size();
4070                 for (int i = 0; i < N; i++) {
4071                     currentPackage = mPackages.get(i);
4072                     if (DEBUG) {
4073                         Slog.i(TAG, "Initiating full-data transport backup of "
4074                                 + currentPackage.packageName);
4075                     }
4076                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE,
4077                             currentPackage.packageName);
4078 
4079                     transportPipes = ParcelFileDescriptor.createPipe();
4080 
4081                     // Tell the transport the data's coming
4082                     int result = transport.performFullBackup(currentPackage,
4083                             transportPipes[0]);
4084                     if (result == BackupTransport.TRANSPORT_OK) {
4085                         // The transport has its own copy of the read end of the pipe,
4086                         // so close ours now
4087                         transportPipes[0].close();
4088                         transportPipes[0] = null;
4089 
4090                         // Now set up the backup engine / data source end of things
4091                         enginePipes = ParcelFileDescriptor.createPipe();
4092                         CountDownLatch runnerLatch = new CountDownLatch(1);
4093                         SinglePackageBackupRunner backupRunner =
4094                                 new SinglePackageBackupRunner(enginePipes[1], currentPackage,
4095                                         transport, runnerLatch);
4096                         // The runner dup'd the pipe half, so we close it here
4097                         enginePipes[1].close();
4098                         enginePipes[1] = null;
4099 
4100                         // Spin off the runner to fetch the app's data and pipe it
4101                         // into the engine pipes
4102                         (new Thread(backupRunner, "package-backup-bridge")).start();
4103 
4104                         // Read data off the engine pipe and pass it to the transport
4105                         // pipe until we hit EOD on the input stream.
4106                         FileInputStream in = new FileInputStream(
4107                                 enginePipes[0].getFileDescriptor());
4108                         FileOutputStream out = new FileOutputStream(
4109                                 transportPipes[1].getFileDescriptor());
4110                         byte[] buffer = new byte[8192];
4111                         int nRead = 0;
4112                         do {
4113                             if (!mKeepRunning.get()) {
4114                                 if (DEBUG_SCHEDULING) {
4115                                     Slog.i(TAG, "Full backup task told to stop");
4116                                 }
4117                                 break;
4118                             }
4119                             nRead = in.read(buffer);
4120                             if (MORE_DEBUG) {
4121                                 Slog.v(TAG, "in.read(buffer) from app: " + nRead);
4122                             }
4123                             if (nRead > 0) {
4124                                 out.write(buffer, 0, nRead);
4125                                 result = transport.sendBackupData(nRead);
4126                             }
4127                         } while (nRead > 0 && result == BackupTransport.TRANSPORT_OK);
4128 
4129                         // If we've lost our running criteria, tell the transport to cancel
4130                         // and roll back this (partial) backup payload; otherwise tell it
4131                         // that we've reached the clean finish state.
4132                         if (!mKeepRunning.get()) {
4133                             result = BackupTransport.TRANSPORT_ERROR;
4134                             transport.cancelFullBackup();
4135                         } else {
4136                             // If we were otherwise in a good state, now interpret the final
4137                             // result based on what finishBackup() returns.  If we're in a
4138                             // failure case already, preserve that result and ignore whatever
4139                             // finishBackup() reports.
4140                             final int finishResult = transport.finishBackup();
4141                             if (result == BackupTransport.TRANSPORT_OK) {
4142                                 result = finishResult;
4143                             }
4144                         }
4145 
4146                         if (MORE_DEBUG) {
4147                             Slog.i(TAG, "Done trying to send backup data: result=" + result);
4148                         }
4149 
4150                         if (result != BackupTransport.TRANSPORT_OK) {
4151                             Slog.e(TAG, "Error " + result
4152                                     + " backing up " + currentPackage.packageName);
4153                         }
4154 
4155                         // Also ask the transport how long it wants us to wait before
4156                         // moving on to the next package, if any.
4157                         backoff = transport.requestFullBackupTime();
4158                         if (DEBUG_SCHEDULING) {
4159                             Slog.i(TAG, "Transport suggested backoff=" + backoff);
4160                         }
4161 
4162                     }
4163 
4164                     // Roll this package to the end of the backup queue if we're
4165                     // in a queue-driven mode (regardless of success/failure)
4166                     if (mUpdateSchedule) {
4167                         enqueueFullBackup(currentPackage.packageName,
4168                                 System.currentTimeMillis());
4169                     }
4170 
4171                     if (result == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
4172                         if (DEBUG) {
4173                             Slog.i(TAG, "Transport rejected backup of "
4174                                     + currentPackage.packageName
4175                                     + ", skipping");
4176                         }
4177                         EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE,
4178                                 currentPackage.packageName, "transport rejected");
4179                         // do nothing, clean up, and continue looping
4180                     } else if (result != BackupTransport.TRANSPORT_OK) {
4181                         Slog.w(TAG, "Transport failed; aborting backup: " + result);
4182                         EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
4183                         return;
4184                     } else {
4185                         // Success!
4186                         EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS,
4187                                 currentPackage.packageName);
4188                         logBackupComplete(currentPackage.packageName);
4189                     }
4190                     cleanUpPipes(transportPipes);
4191                     cleanUpPipes(enginePipes);
4192                     currentPackage = null;
4193                 }
4194 
4195                 if (DEBUG) {
4196                     Slog.i(TAG, "Full backup completed.");
4197                 }
4198             } catch (Exception e) {
4199                 Slog.w(TAG, "Exception trying full transport backup", e);
4200             } finally {
4201                 cleanUpPipes(transportPipes);
4202                 cleanUpPipes(enginePipes);
4203 
4204                 if (mJob != null) {
4205                     mJob.finishBackupPass();
4206                 }
4207 
4208                 synchronized (mQueueLock) {
4209                     mRunningFullBackupTask = null;
4210                 }
4211 
4212                 mLatch.countDown();
4213 
4214                 // Now that we're actually done with schedule-driven work, reschedule
4215                 // the next pass based on the new queue state.
4216                 if (mUpdateSchedule) {
4217                     scheduleNextFullBackupJob(backoff);
4218                 }
4219             }
4220         }
4221 
cleanUpPipes(ParcelFileDescriptor[] pipes)4222         void cleanUpPipes(ParcelFileDescriptor[] pipes) {
4223             if (pipes != null) {
4224                 if (pipes[0] != null) {
4225                     ParcelFileDescriptor fd = pipes[0];
4226                     pipes[0] = null;
4227                     try {
4228                         fd.close();
4229                     } catch (IOException e) {
4230                         Slog.w(TAG, "Unable to close pipe!");
4231                     }
4232                 }
4233                 if (pipes[1] != null) {
4234                     ParcelFileDescriptor fd = pipes[1];
4235                     pipes[1] = null;
4236                     try {
4237                         fd.close();
4238                     } catch (IOException e) {
4239                         Slog.w(TAG, "Unable to close pipe!");
4240                     }
4241                 }
4242             }
4243         }
4244 
4245         // Run the backup and pipe it back to the given socket -- expects to run on
4246         // a standalone thread.  The  runner owns this half of the pipe, and closes
4247         // it to indicate EOD to the other end.
4248         class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
4249             final AtomicInteger mResult = new AtomicInteger();
4250             final CountDownLatch mLatch = new CountDownLatch(1);
4251             final IBackupTransport mTransport;
4252 
SinglePackageBackupPreflight(IBackupTransport transport)4253             public SinglePackageBackupPreflight(IBackupTransport transport) {
4254                 mTransport = transport;
4255             }
4256 
4257             @Override
preflightFullBackup(PackageInfo pkg, IBackupAgent agent)4258             public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
4259                 int result;
4260                 try {
4261                     final int token = generateToken();
4262                     prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
4263                     addBackupTrace("preflighting");
4264                     if (MORE_DEBUG) {
4265                         Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
4266                     }
4267                     agent.doMeasureFullBackup(token, mBackupManagerBinder);
4268 
4269                     // now wait to get our result back
4270                     mLatch.await();
4271                     int totalSize = mResult.get();
4272                     if (MORE_DEBUG) {
4273                         Slog.v(TAG, "Got preflight response; size=" + totalSize);
4274                     }
4275 
4276                     result = mTransport.checkFullBackupSize(totalSize);
4277                 } catch (Exception e) {
4278                     Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
4279                     result = BackupTransport.AGENT_ERROR;
4280                 }
4281                 return result;
4282             }
4283 
4284             @Override
execute()4285             public void execute() {
4286                 // Unused in this case
4287             }
4288 
4289             @Override
operationComplete(int result)4290             public void operationComplete(int result) {
4291                 // got the callback, and our preflightFullBackup() method is waiting for the result
4292                 if (MORE_DEBUG) {
4293                     Slog.i(TAG, "Preflight op complete, result=" + result);
4294                 }
4295                 mResult.set(result);
4296                 mLatch.countDown();
4297             }
4298 
4299             @Override
handleTimeout()4300             public void handleTimeout() {
4301                 if (MORE_DEBUG) {
4302                     Slog.i(TAG, "Preflight timeout; failing");
4303                 }
4304                 mResult.set(BackupTransport.AGENT_ERROR);
4305                 mLatch.countDown();
4306             }
4307 
4308         }
4309 
4310         class SinglePackageBackupRunner implements Runnable {
4311             final ParcelFileDescriptor mOutput;
4312             final PackageInfo mTarget;
4313             final FullBackupPreflight mPreflight;
4314             final CountDownLatch mLatch;
4315 
SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target, IBackupTransport transport, CountDownLatch latch)4316             SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
4317                     IBackupTransport transport, CountDownLatch latch) throws IOException {
4318                 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
4319                 mTarget = target;
4320                 mPreflight = new SinglePackageBackupPreflight(transport);
4321                 mLatch = latch;
4322             }
4323 
4324             @Override
run()4325             public void run() {
4326                 try {
4327                     FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
4328                     FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName,
4329                             mPreflight, false);
4330                     engine.backupOnePackage(mTarget);
4331                 } catch (Exception e) {
4332                     Slog.e(TAG, "Exception during full package backup of " + mTarget);
4333                 } finally {
4334                     mLatch.countDown();
4335                     try {
4336                         mOutput.close();
4337                     } catch (IOException e) {
4338                         Slog.w(TAG, "Error closing transport pipe in runner");
4339                     }
4340                 }
4341             }
4342         }
4343     }
4344 
4345     // ----- Full-data backup scheduling -----
4346 
4347     /**
4348      * Schedule a job to tell us when it's a good time to run a full backup
4349      */
scheduleNextFullBackupJob(long transportMinLatency)4350     void scheduleNextFullBackupJob(long transportMinLatency) {
4351         synchronized (mQueueLock) {
4352             if (mFullBackupQueue.size() > 0) {
4353                 // schedule the next job at the point in the future when the least-recently
4354                 // backed up app comes due for backup again; or immediately if it's already
4355                 // due.
4356                 final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
4357                 final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
4358                 final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
4359                         ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
4360                 final long latency = Math.max(transportMinLatency, appLatency);
4361                 Runnable r = new Runnable() {
4362                     @Override public void run() {
4363                         FullBackupJob.schedule(mContext, latency);
4364                     }
4365                 };
4366                 mBackupHandler.postDelayed(r, 2500);
4367             } else {
4368                 if (DEBUG_SCHEDULING) {
4369                     Slog.i(TAG, "Full backup queue empty; not scheduling");
4370                 }
4371             }
4372         }
4373     }
4374 
4375     /**
4376      * Enqueue full backup for the given app, with a note about when it last ran.
4377      */
enqueueFullBackup(String packageName, long lastBackedUp)4378     void enqueueFullBackup(String packageName, long lastBackedUp) {
4379         FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
4380         synchronized (mQueueLock) {
4381             int N = mFullBackupQueue.size();
4382             // First, sanity check that we aren't adding a duplicate.  Slow but
4383             // straightforward; we'll have at most on the order of a few hundred
4384             // items in this list.
4385             for (int i = N-1; i >= 0; i--) {
4386                 final FullBackupEntry e = mFullBackupQueue.get(i);
4387                 if (packageName.equals(e.packageName)) {
4388                     mFullBackupQueue.remove(i);
4389                 }
4390             }
4391 
4392             // This is also slow but easy for modest numbers of apps: work backwards
4393             // from the end of the queue until we find an item whose last backup
4394             // time was before this one, then insert this new entry after it.  If we're
4395             // adding something new we don't bother scanning, and just prepend.
4396             int which = -1;
4397             if (lastBackedUp > 0) {
4398                 for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
4399                     final FullBackupEntry entry = mFullBackupQueue.get(which);
4400                     if (entry.lastBackup <= lastBackedUp) {
4401                         mFullBackupQueue.add(which + 1, newEntry);
4402                         break;
4403                     }
4404                 }
4405             }
4406             if (which < 0) {
4407                 // this one is earlier than any existing one, so prepend
4408                 mFullBackupQueue.add(0, newEntry);
4409             }
4410         }
4411         writeFullBackupScheduleAsync();
4412     }
4413 
fullBackupAllowable(IBackupTransport transport)4414     private boolean fullBackupAllowable(IBackupTransport transport) {
4415         if (transport == null) {
4416             Slog.w(TAG, "Transport not present; full data backup not performed");
4417             return false;
4418         }
4419 
4420         // Don't proceed unless we have already established package metadata
4421         // for the current dataset via a key/value backup pass.
4422         try {
4423             File stateDir = new File(mBaseStateDir, transport.transportDirName());
4424             File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
4425             if (pmState.length() <= 0) {
4426                 if (DEBUG) {
4427                     Slog.i(TAG, "Full backup requested but dataset not yet initialized");
4428                 }
4429                 return false;
4430             }
4431         } catch (Exception e) {
4432             Slog.w(TAG, "Unable to contact transport");
4433             return false;
4434         }
4435 
4436         return true;
4437     }
4438 
4439     /**
4440      * Conditions are right for a full backup operation, so run one.  The model we use is
4441      * to perform one app backup per scheduled job execution, and to reschedule the job
4442      * with zero latency as long as conditions remain right and we still have work to do.
4443      *
4444      * <p>This is the "start a full backup operation" entry point called by the scheduled job.
4445      *
4446      * @return Whether ongoing work will continue.  The return value here will be passed
4447      *         along as the return value to the scheduled job's onStartJob() callback.
4448      */
beginFullBackup(FullBackupJob scheduledJob)4449     boolean beginFullBackup(FullBackupJob scheduledJob) {
4450         long now = System.currentTimeMillis();
4451         FullBackupEntry entry = null;
4452         long latency = MIN_FULL_BACKUP_INTERVAL;
4453 
4454         if (!mEnabled || !mProvisioned) {
4455             // Backups are globally disabled, so don't proceed.  We also don't reschedule
4456             // the job driving automatic backups; that job will be scheduled again when
4457             // the user enables backup.
4458             if (MORE_DEBUG) {
4459                 Slog.i(TAG, "beginFullBackup but e=" + mEnabled
4460                         + " p=" + mProvisioned + "; ignoring");
4461             }
4462             return false;
4463         }
4464 
4465         // Don't run the backup if we're in battery saver mode, but reschedule
4466         // to try again in the not-so-distant future.
4467         if (mPowerManager.isPowerSaveMode()) {
4468             if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
4469             FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL);
4470             return false;
4471         }
4472 
4473         if (DEBUG_SCHEDULING) {
4474             Slog.i(TAG, "Beginning scheduled full backup operation");
4475         }
4476 
4477         // Great; we're able to run full backup jobs now.  See if we have any work to do.
4478         synchronized (mQueueLock) {
4479             if (mRunningFullBackupTask != null) {
4480                 Slog.e(TAG, "Backup triggered but one already/still running!");
4481                 return false;
4482             }
4483 
4484             if (mFullBackupQueue.size() == 0) {
4485                 // no work to do so just bow out
4486                 if (DEBUG) {
4487                     Slog.i(TAG, "Backup queue empty; doing nothing");
4488                 }
4489                 return false;
4490             }
4491 
4492             // At this point we know that we have work to do, just not right now.  Any
4493             // exit without actually running backups will also require that we
4494             // reschedule the job.
4495             boolean runBackup = true;
4496 
4497             if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
4498                 if (MORE_DEBUG) {
4499                     Slog.i(TAG, "Preconditions not met; not running full backup");
4500                 }
4501                 runBackup = false;
4502                 // Typically this means we haven't run a key/value backup yet.  Back off
4503                 // full-backup operations by the key/value job's run interval so that
4504                 // next time we run, we are likely to be able to make progress.
4505                 latency = KeyValueBackupJob.BATCH_INTERVAL;
4506             }
4507 
4508             if (runBackup) {
4509                 entry = mFullBackupQueue.get(0);
4510                 long timeSinceRun = now - entry.lastBackup;
4511                 runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
4512                 if (!runBackup) {
4513                     // It's too early to back up the next thing in the queue, so bow out
4514                     if (MORE_DEBUG) {
4515                         Slog.i(TAG, "Device ready but too early to back up next app");
4516                     }
4517                     // Wait until the next app in the queue falls due for a full data backup
4518                     latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
4519                 }
4520             }
4521 
4522             if (!runBackup) {
4523                 if (DEBUG_SCHEDULING) {
4524                     Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
4525                 }
4526                 final long deferTime = latency;     // pin for the closure
4527                 mBackupHandler.post(new Runnable() {
4528                     @Override public void run() {
4529                         FullBackupJob.schedule(mContext, deferTime);
4530                     }
4531                 });
4532                 return false;
4533             }
4534 
4535             // Okay, the top thing is runnable now.  Pop it off and get going.
4536             mFullBackupQueue.remove(0);
4537             CountDownLatch latch = new CountDownLatch(1);
4538             String[] pkg = new String[] {entry.packageName};
4539             mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
4540                     scheduledJob, latch);
4541             (new Thread(mRunningFullBackupTask)).start();
4542         }
4543 
4544         return true;
4545     }
4546 
4547     // The job scheduler says our constraints don't hold any more,
4548     // so tear down any ongoing backup task right away.
endFullBackup()4549     void endFullBackup() {
4550         synchronized (mQueueLock) {
4551             if (mRunningFullBackupTask != null) {
4552                 if (DEBUG_SCHEDULING) {
4553                     Slog.i(TAG, "Telling running backup to stop");
4554                 }
4555                 mRunningFullBackupTask.setRunning(false);
4556             }
4557         }
4558     }
4559 
4560     // ----- Restore infrastructure -----
4561 
4562     abstract class RestoreEngine {
4563         static final String TAG = "RestoreEngine";
4564 
4565         public static final int SUCCESS = 0;
4566         public static final int TARGET_FAILURE = -2;
4567         public static final int TRANSPORT_FAILURE = -3;
4568 
4569         private AtomicBoolean mRunning = new AtomicBoolean(false);
4570         private AtomicInteger mResult = new AtomicInteger(SUCCESS);
4571 
isRunning()4572         public boolean isRunning() {
4573             return mRunning.get();
4574         }
4575 
setRunning(boolean stillRunning)4576         public void setRunning(boolean stillRunning) {
4577             synchronized (mRunning) {
4578                 mRunning.set(stillRunning);
4579                 mRunning.notifyAll();
4580             }
4581         }
4582 
waitForResult()4583         public int waitForResult() {
4584             synchronized (mRunning) {
4585                 while (isRunning()) {
4586                     try {
4587                         mRunning.wait();
4588                     } catch (InterruptedException e) {}
4589                 }
4590             }
4591             return getResult();
4592         }
4593 
getResult()4594         public int getResult() {
4595             return mResult.get();
4596         }
4597 
setResult(int result)4598         public void setResult(int result) {
4599             mResult.set(result);
4600         }
4601 
4602         // TODO: abstract restore state and APIs
4603     }
4604 
4605     // ----- Full restore from a file/socket -----
4606 
4607     // Description of a file in the restore datastream
4608     static class FileMetadata {
4609         String packageName;             // name of the owning app
4610         String installerPackageName;    // name of the market-type app that installed the owner
4611         int type;                       // e.g. BackupAgent.TYPE_DIRECTORY
4612         String domain;                  // e.g. FullBackup.DATABASE_TREE_TOKEN
4613         String path;                    // subpath within the semantic domain
4614         long mode;                      // e.g. 0666 (actually int)
4615         long mtime;                     // last mod time, UTC time_t (actually int)
4616         long size;                      // bytes of content
4617 
4618         @Override
toString()4619         public String toString() {
4620             StringBuilder sb = new StringBuilder(128);
4621             sb.append("FileMetadata{");
4622             sb.append(packageName); sb.append(',');
4623             sb.append(type); sb.append(',');
4624             sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
4625             sb.append(size);
4626             sb.append('}');
4627             return sb.toString();
4628         }
4629     }
4630 
4631     enum RestorePolicy {
4632         IGNORE,
4633         ACCEPT,
4634         ACCEPT_IF_APK
4635     }
4636 
4637     // Full restore engine, used by both adb restore and transport-based full restore
4638     class FullRestoreEngine extends RestoreEngine {
4639         // Dedicated observer, if any
4640         IFullBackupRestoreObserver mObserver;
4641 
4642         // Where we're delivering the file data as we go
4643         IBackupAgent mAgent;
4644 
4645         // Are we permitted to only deliver a specific package's metadata?
4646         PackageInfo mOnlyPackage;
4647 
4648         boolean mAllowApks;
4649         boolean mAllowObbs;
4650 
4651         // Which package are we currently handling data for?
4652         String mAgentPackage;
4653 
4654         // Info for working with the target app process
4655         ApplicationInfo mTargetApp;
4656 
4657         // Machinery for restoring OBBs
4658         FullBackupObbConnection mObbConnection = null;
4659 
4660         // possible handling states for a given package in the restore dataset
4661         final HashMap<String, RestorePolicy> mPackagePolicies
4662                 = new HashMap<String, RestorePolicy>();
4663 
4664         // installer package names for each encountered app, derived from the manifests
4665         final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
4666 
4667         // Signatures for a given package found in its manifest file
4668         final HashMap<String, Signature[]> mManifestSignatures
4669                 = new HashMap<String, Signature[]>();
4670 
4671         // Packages we've already wiped data on when restoring their first file
4672         final HashSet<String> mClearedPackages = new HashSet<String>();
4673 
4674         // How much data have we moved?
4675         long mBytes;
4676 
4677         // Working buffer
4678         byte[] mBuffer;
4679 
4680         // Pipes for moving data
4681         ParcelFileDescriptor[] mPipes = null;
4682 
4683         // Widget blob to be restored out-of-band
4684         byte[] mWidgetData = null;
4685 
4686         // Runner that can be placed in a separate thread to do in-process
4687         // invocations of the full restore API asynchronously
4688         class RestoreFileRunnable implements Runnable {
4689             IBackupAgent mAgent;
4690             FileMetadata mInfo;
4691             ParcelFileDescriptor mSocket;
4692             int mToken;
4693 
RestoreFileRunnable(IBackupAgent agent, FileMetadata info, ParcelFileDescriptor socket, int token)4694             RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
4695                     ParcelFileDescriptor socket, int token) throws IOException {
4696                 mAgent = agent;
4697                 mInfo = info;
4698                 mToken = token;
4699 
4700                 // This class is used strictly for process-local binder invocations.  The
4701                 // semantics of ParcelFileDescriptor differ in this case; in particular, we
4702                 // do not automatically get a 'dup'ed descriptor that we can can continue
4703                 // to use asynchronously from the caller.  So, we make sure to dup it ourselves
4704                 // before proceeding to do the restore.
4705                 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
4706             }
4707 
4708             @Override
run()4709             public void run() {
4710                 try {
4711                     mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
4712                             mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
4713                             mToken, mBackupManagerBinder);
4714                 } catch (RemoteException e) {
4715                     // never happens; this is used strictly for local binder calls
4716                 }
4717             }
4718         }
4719 
FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage, boolean allowApks, boolean allowObbs)4720         public FullRestoreEngine(IFullBackupRestoreObserver observer, PackageInfo onlyPackage,
4721                 boolean allowApks, boolean allowObbs) {
4722             mObserver = observer;
4723             mOnlyPackage = onlyPackage;
4724             mAllowApks = allowApks;
4725             mAllowObbs = allowObbs;
4726             mBuffer = new byte[32 * 1024];
4727             mBytes = 0;
4728         }
4729 
getAgent()4730         public IBackupAgent getAgent() {
4731             return mAgent;
4732         }
4733 
restoreOneFile(InputStream instream, boolean mustKillAgent)4734         public boolean restoreOneFile(InputStream instream, boolean mustKillAgent) {
4735             if (!isRunning()) {
4736                 Slog.w(TAG, "Restore engine used after halting");
4737                 return false;
4738             }
4739 
4740             FileMetadata info;
4741             try {
4742                 if (MORE_DEBUG) {
4743                     Slog.v(TAG, "Reading tar header for restoring file");
4744                 }
4745                 info = readTarHeaders(instream);
4746                 if (info != null) {
4747                     if (MORE_DEBUG) {
4748                         dumpFileMetadata(info);
4749                     }
4750 
4751                     final String pkg = info.packageName;
4752                     if (!pkg.equals(mAgentPackage)) {
4753                         // In the single-package case, it's a semantic error to expect
4754                         // one app's data but see a different app's on the wire
4755                         if (mOnlyPackage != null) {
4756                             if (!pkg.equals(mOnlyPackage.packageName)) {
4757                                 Slog.w(TAG, "Expected data for " + mOnlyPackage
4758                                         + " but saw " + pkg);
4759                                 setResult(RestoreEngine.TRANSPORT_FAILURE);
4760                                 setRunning(false);
4761                                 return false;
4762                             }
4763                         }
4764 
4765                         // okay, change in package; set up our various
4766                         // bookkeeping if we haven't seen it yet
4767                         if (!mPackagePolicies.containsKey(pkg)) {
4768                             mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4769                         }
4770 
4771                         // Clean up the previous agent relationship if necessary,
4772                         // and let the observer know we're considering a new app.
4773                         if (mAgent != null) {
4774                             if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
4775                             // Now we're really done
4776                             tearDownPipes();
4777                             tearDownAgent(mTargetApp);
4778                             mTargetApp = null;
4779                             mAgentPackage = null;
4780                         }
4781                     }
4782 
4783                     if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
4784                         mPackagePolicies.put(pkg, readAppManifest(info, instream));
4785                         mPackageInstallers.put(pkg, info.installerPackageName);
4786                         // We've read only the manifest content itself at this point,
4787                         // so consume the footer before looping around to the next
4788                         // input file
4789                         skipTarPadding(info.size, instream);
4790                         sendOnRestorePackage(pkg);
4791                     } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
4792                         // Metadata blobs!
4793                         readMetadata(info, instream);
4794                         skipTarPadding(info.size, instream);
4795                     } else {
4796                         // Non-manifest, so it's actual file data.  Is this a package
4797                         // we're ignoring?
4798                         boolean okay = true;
4799                         RestorePolicy policy = mPackagePolicies.get(pkg);
4800                         switch (policy) {
4801                             case IGNORE:
4802                                 okay = false;
4803                                 break;
4804 
4805                             case ACCEPT_IF_APK:
4806                                 // If we're in accept-if-apk state, then the first file we
4807                                 // see MUST be the apk.
4808                                 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
4809                                     if (DEBUG) Slog.d(TAG, "APK file; installing");
4810                                     // Try to install the app.
4811                                     String installerName = mPackageInstallers.get(pkg);
4812                                     okay = installApk(info, installerName, instream);
4813                                     // good to go; promote to ACCEPT
4814                                     mPackagePolicies.put(pkg, (okay)
4815                                             ? RestorePolicy.ACCEPT
4816                                                     : RestorePolicy.IGNORE);
4817                                     // At this point we've consumed this file entry
4818                                     // ourselves, so just strip the tar footer and
4819                                     // go on to the next file in the input stream
4820                                     skipTarPadding(info.size, instream);
4821                                     return true;
4822                                 } else {
4823                                     // File data before (or without) the apk.  We can't
4824                                     // handle it coherently in this case so ignore it.
4825                                     mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4826                                     okay = false;
4827                                 }
4828                                 break;
4829 
4830                             case ACCEPT:
4831                                 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
4832                                     if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
4833                                     // we can take the data without the apk, so we
4834                                     // *want* to do so.  skip the apk by declaring this
4835                                     // one file not-okay without changing the restore
4836                                     // policy for the package.
4837                                     okay = false;
4838                                 }
4839                                 break;
4840 
4841                             default:
4842                                 // Something has gone dreadfully wrong when determining
4843                                 // the restore policy from the manifest.  Ignore the
4844                                 // rest of this package's data.
4845                                 Slog.e(TAG, "Invalid policy from manifest");
4846                                 okay = false;
4847                                 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4848                                 break;
4849                         }
4850 
4851                         // Is it a *file* we need to drop?
4852                         if (!isRestorableFile(info)) {
4853                             okay = false;
4854                         }
4855 
4856                         // If the policy is satisfied, go ahead and set up to pipe the
4857                         // data to the agent.
4858                         if (DEBUG && okay && mAgent != null) {
4859                             Slog.i(TAG, "Reusing existing agent instance");
4860                         }
4861                         if (okay && mAgent == null) {
4862                             if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
4863 
4864                             try {
4865                                 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
4866 
4867                                 // If we haven't sent any data to this app yet, we probably
4868                                 // need to clear it first.  Check that.
4869                                 if (!mClearedPackages.contains(pkg)) {
4870                                     // apps with their own backup agents are
4871                                     // responsible for coherently managing a full
4872                                     // restore.
4873                                     if (mTargetApp.backupAgentName == null) {
4874                                         if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
4875                                         clearApplicationDataSynchronous(pkg);
4876                                     } else {
4877                                         if (MORE_DEBUG) Slog.d(TAG, "backup agent ("
4878                                                 + mTargetApp.backupAgentName + ") => no clear");
4879                                     }
4880                                     mClearedPackages.add(pkg);
4881                                 } else {
4882                                     if (MORE_DEBUG) {
4883                                         Slog.d(TAG, "We've initialized this app already; no clear required");
4884                                     }
4885                                 }
4886 
4887                                 // All set; now set up the IPC and launch the agent
4888                                 setUpPipes();
4889                                 mAgent = bindToAgentSynchronous(mTargetApp,
4890                                         IApplicationThread.BACKUP_MODE_RESTORE_FULL);
4891                                 mAgentPackage = pkg;
4892                             } catch (IOException e) {
4893                                 // fall through to error handling
4894                             } catch (NameNotFoundException e) {
4895                                 // fall through to error handling
4896                             }
4897 
4898                             if (mAgent == null) {
4899                                 Slog.e(TAG, "Unable to create agent for " + pkg);
4900                                 okay = false;
4901                                 tearDownPipes();
4902                                 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
4903                             }
4904                         }
4905 
4906                         // Sanity check: make sure we never give data to the wrong app.  This
4907                         // should never happen but a little paranoia here won't go amiss.
4908                         if (okay && !pkg.equals(mAgentPackage)) {
4909                             Slog.e(TAG, "Restoring data for " + pkg
4910                                     + " but agent is for " + mAgentPackage);
4911                             okay = false;
4912                         }
4913 
4914                         // At this point we have an agent ready to handle the full
4915                         // restore data as well as a pipe for sending data to
4916                         // that agent.  Tell the agent to start reading from the
4917                         // pipe.
4918                         if (okay) {
4919                             boolean agentSuccess = true;
4920                             long toCopy = info.size;
4921                             final int token = generateToken();
4922                             try {
4923                                 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
4924                                 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
4925                                     if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
4926                                             + " : " + info.path);
4927                                     mObbConnection.restoreObbFile(pkg, mPipes[0],
4928                                             info.size, info.type, info.path, info.mode,
4929                                             info.mtime, token, mBackupManagerBinder);
4930                                 } else {
4931                                     if (MORE_DEBUG) Slog.d(TAG, "Invoking agent to restore file "
4932                                             + info.path);
4933                                     // fire up the app's agent listening on the socket.  If
4934                                     // the agent is running in the system process we can't
4935                                     // just invoke it asynchronously, so we provide a thread
4936                                     // for it here.
4937                                     if (mTargetApp.processName.equals("system")) {
4938                                         Slog.d(TAG, "system process agent - spinning a thread");
4939                                         RestoreFileRunnable runner = new RestoreFileRunnable(
4940                                                 mAgent, info, mPipes[0], token);
4941                                         new Thread(runner, "restore-sys-runner").start();
4942                                     } else {
4943                                         mAgent.doRestoreFile(mPipes[0], info.size, info.type,
4944                                                 info.domain, info.path, info.mode, info.mtime,
4945                                                 token, mBackupManagerBinder);
4946                                     }
4947                                 }
4948                             } catch (IOException e) {
4949                                 // couldn't dup the socket for a process-local restore
4950                                 Slog.d(TAG, "Couldn't establish restore");
4951                                 agentSuccess = false;
4952                                 okay = false;
4953                             } catch (RemoteException e) {
4954                                 // whoops, remote entity went away.  We'll eat the content
4955                                 // ourselves, then, and not copy it over.
4956                                 Slog.e(TAG, "Agent crashed during full restore");
4957                                 agentSuccess = false;
4958                                 okay = false;
4959                             }
4960 
4961                             // Copy over the data if the agent is still good
4962                             if (okay) {
4963                                 if (MORE_DEBUG) {
4964                                     Slog.v(TAG, "  copying to restore agent: "
4965                                             + toCopy + " bytes");
4966                                 }
4967                                 boolean pipeOkay = true;
4968                                 FileOutputStream pipe = new FileOutputStream(
4969                                         mPipes[1].getFileDescriptor());
4970                                 while (toCopy > 0) {
4971                                     int toRead = (toCopy > mBuffer.length)
4972                                             ? mBuffer.length : (int)toCopy;
4973                                     int nRead = instream.read(mBuffer, 0, toRead);
4974                                     if (nRead >= 0) mBytes += nRead;
4975                                     if (nRead <= 0) break;
4976                                     toCopy -= nRead;
4977 
4978                                     // send it to the output pipe as long as things
4979                                     // are still good
4980                                     if (pipeOkay) {
4981                                         try {
4982                                             pipe.write(mBuffer, 0, nRead);
4983                                         } catch (IOException e) {
4984                                             Slog.e(TAG, "Failed to write to restore pipe", e);
4985                                             pipeOkay = false;
4986                                         }
4987                                     }
4988                                 }
4989 
4990                                 // done sending that file!  Now we just need to consume
4991                                 // the delta from info.size to the end of block.
4992                                 skipTarPadding(info.size, instream);
4993 
4994                                 // and now that we've sent it all, wait for the remote
4995                                 // side to acknowledge receipt
4996                                 agentSuccess = waitUntilOperationComplete(token);
4997                             }
4998 
4999                             // okay, if the remote end failed at any point, deal with
5000                             // it by ignoring the rest of the restore on it
5001                             if (!agentSuccess) {
5002                                 Slog.w(TAG, "Agent failure; ending restore");
5003                                 mBackupHandler.removeMessages(MSG_TIMEOUT);
5004                                 tearDownPipes();
5005                                 tearDownAgent(mTargetApp);
5006                                 mAgent = null;
5007                                 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
5008 
5009                                 // If this was a single-package restore, we halt immediately
5010                                 // with an agent error under these circumstances
5011                                 if (mOnlyPackage != null) {
5012                                     setResult(RestoreEngine.TARGET_FAILURE);
5013                                     setRunning(false);
5014                                     return false;
5015                                 }
5016                             }
5017                         }
5018 
5019                         // Problems setting up the agent communication, an explicitly
5020                         // dropped file, or an already-ignored package: skip to the
5021                         // next stream entry by reading and discarding this file.
5022                         if (!okay) {
5023                             if (MORE_DEBUG) Slog.d(TAG, "[discarding file content]");
5024                             long bytesToConsume = (info.size + 511) & ~511;
5025                             while (bytesToConsume > 0) {
5026                                 int toRead = (bytesToConsume > mBuffer.length)
5027                                         ? mBuffer.length : (int)bytesToConsume;
5028                                 long nRead = instream.read(mBuffer, 0, toRead);
5029                                 if (nRead >= 0) mBytes += nRead;
5030                                 if (nRead <= 0) break;
5031                                 bytesToConsume -= nRead;
5032                             }
5033                         }
5034                     }
5035                 }
5036             } catch (IOException e) {
5037                 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
5038                 setResult(RestoreEngine.TRANSPORT_FAILURE);
5039                 info = null;
5040             }
5041 
5042             // If we got here we're either running smoothly or we've finished
5043             if (info == null) {
5044                 if (MORE_DEBUG) {
5045                     Slog.i(TAG, "No [more] data for this package; tearing down");
5046                 }
5047                 tearDownPipes();
5048                 setRunning(false);
5049                 if (mustKillAgent) {
5050                     tearDownAgent(mTargetApp);
5051                 }
5052             }
5053             return (info != null);
5054         }
5055 
setUpPipes()5056         void setUpPipes() throws IOException {
5057             mPipes = ParcelFileDescriptor.createPipe();
5058         }
5059 
tearDownPipes()5060         void tearDownPipes() {
5061             if (mPipes != null) {
5062                 try {
5063                     mPipes[0].close();
5064                     mPipes[0] = null;
5065                     mPipes[1].close();
5066                     mPipes[1] = null;
5067                 } catch (IOException e) {
5068                     Slog.w(TAG, "Couldn't close agent pipes", e);
5069                 }
5070                 mPipes = null;
5071             }
5072         }
5073 
tearDownAgent(ApplicationInfo app)5074         void tearDownAgent(ApplicationInfo app) {
5075             if (mAgent != null) {
5076                 tearDownAgentAndKill(app);
5077                 mAgent = null;
5078             }
5079         }
5080 
5081         class RestoreInstallObserver extends PackageInstallObserver {
5082             final AtomicBoolean mDone = new AtomicBoolean();
5083             String mPackageName;
5084             int mResult;
5085 
reset()5086             public void reset() {
5087                 synchronized (mDone) {
5088                     mDone.set(false);
5089                 }
5090             }
5091 
waitForCompletion()5092             public void waitForCompletion() {
5093                 synchronized (mDone) {
5094                     while (mDone.get() == false) {
5095                         try {
5096                             mDone.wait();
5097                         } catch (InterruptedException e) { }
5098                     }
5099                 }
5100             }
5101 
getResult()5102             int getResult() {
5103                 return mResult;
5104             }
5105 
5106             @Override
onPackageInstalled(String packageName, int returnCode, String msg, Bundle extras)5107             public void onPackageInstalled(String packageName, int returnCode,
5108                     String msg, Bundle extras) {
5109                 synchronized (mDone) {
5110                     mResult = returnCode;
5111                     mPackageName = packageName;
5112                     mDone.set(true);
5113                     mDone.notifyAll();
5114                 }
5115             }
5116         }
5117 
5118         class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
5119             final AtomicBoolean mDone = new AtomicBoolean();
5120             int mResult;
5121 
reset()5122             public void reset() {
5123                 synchronized (mDone) {
5124                     mDone.set(false);
5125                 }
5126             }
5127 
waitForCompletion()5128             public void waitForCompletion() {
5129                 synchronized (mDone) {
5130                     while (mDone.get() == false) {
5131                         try {
5132                             mDone.wait();
5133                         } catch (InterruptedException e) { }
5134                     }
5135                 }
5136             }
5137 
5138             @Override
packageDeleted(String packageName, int returnCode)5139             public void packageDeleted(String packageName, int returnCode) throws RemoteException {
5140                 synchronized (mDone) {
5141                     mResult = returnCode;
5142                     mDone.set(true);
5143                     mDone.notifyAll();
5144                 }
5145             }
5146         }
5147 
5148         final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
5149         final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
5150 
installApk(FileMetadata info, String installerPackage, InputStream instream)5151         boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
5152             boolean okay = true;
5153 
5154             if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
5155 
5156             // The file content is an .apk file.  Copy it out to a staging location and
5157             // attempt to install it.
5158             File apkFile = new File(mDataDir, info.packageName);
5159             try {
5160                 FileOutputStream apkStream = new FileOutputStream(apkFile);
5161                 byte[] buffer = new byte[32 * 1024];
5162                 long size = info.size;
5163                 while (size > 0) {
5164                     long toRead = (buffer.length < size) ? buffer.length : size;
5165                     int didRead = instream.read(buffer, 0, (int)toRead);
5166                     if (didRead >= 0) mBytes += didRead;
5167                     apkStream.write(buffer, 0, didRead);
5168                     size -= didRead;
5169                 }
5170                 apkStream.close();
5171 
5172                 // make sure the installer can read it
5173                 apkFile.setReadable(true, false);
5174 
5175                 // Now install it
5176                 Uri packageUri = Uri.fromFile(apkFile);
5177                 mInstallObserver.reset();
5178                 mPackageManager.installPackage(packageUri, mInstallObserver,
5179                         PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
5180                         installerPackage);
5181                 mInstallObserver.waitForCompletion();
5182 
5183                 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
5184                     // The only time we continue to accept install of data even if the
5185                     // apk install failed is if we had already determined that we could
5186                     // accept the data regardless.
5187                     if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
5188                         okay = false;
5189                     }
5190                 } else {
5191                     // Okay, the install succeeded.  Make sure it was the right app.
5192                     boolean uninstall = false;
5193                     if (!mInstallObserver.mPackageName.equals(info.packageName)) {
5194                         Slog.w(TAG, "Restore stream claimed to include apk for "
5195                                 + info.packageName + " but apk was really "
5196                                 + mInstallObserver.mPackageName);
5197                         // delete the package we just put in place; it might be fraudulent
5198                         okay = false;
5199                         uninstall = true;
5200                     } else {
5201                         try {
5202                             PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
5203                                     PackageManager.GET_SIGNATURES);
5204                             if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
5205                                 Slog.w(TAG, "Restore stream contains apk of package "
5206                                         + info.packageName + " but it disallows backup/restore");
5207                                 okay = false;
5208                             } else {
5209                                 // So far so good -- do the signatures match the manifest?
5210                                 Signature[] sigs = mManifestSignatures.get(info.packageName);
5211                                 if (signaturesMatch(sigs, pkg)) {
5212                                     // If this is a system-uid app without a declared backup agent,
5213                                     // don't restore any of the file data.
5214                                     if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
5215                                             && (pkg.applicationInfo.backupAgentName == null)) {
5216                                         Slog.w(TAG, "Installed app " + info.packageName
5217                                                 + " has restricted uid and no agent");
5218                                         okay = false;
5219                                     }
5220                                 } else {
5221                                     Slog.w(TAG, "Installed app " + info.packageName
5222                                             + " signatures do not match restore manifest");
5223                                     okay = false;
5224                                     uninstall = true;
5225                                 }
5226                             }
5227                         } catch (NameNotFoundException e) {
5228                             Slog.w(TAG, "Install of package " + info.packageName
5229                                     + " succeeded but now not found");
5230                             okay = false;
5231                         }
5232                     }
5233 
5234                     // If we're not okay at this point, we need to delete the package
5235                     // that we just installed.
5236                     if (uninstall) {
5237                         mDeleteObserver.reset();
5238                         mPackageManager.deletePackage(mInstallObserver.mPackageName,
5239                                 mDeleteObserver, 0);
5240                         mDeleteObserver.waitForCompletion();
5241                     }
5242                 }
5243             } catch (IOException e) {
5244                 Slog.e(TAG, "Unable to transcribe restored apk for install");
5245                 okay = false;
5246             } finally {
5247                 apkFile.delete();
5248             }
5249 
5250             return okay;
5251         }
5252 
5253         // Given an actual file content size, consume the post-content padding mandated
5254         // by the tar format.
skipTarPadding(long size, InputStream instream)5255         void skipTarPadding(long size, InputStream instream) throws IOException {
5256             long partial = (size + 512) % 512;
5257             if (partial > 0) {
5258                 final int needed = 512 - (int)partial;
5259                 if (MORE_DEBUG) {
5260                     Slog.i(TAG, "Skipping tar padding: " + needed + " bytes");
5261                 }
5262                 byte[] buffer = new byte[needed];
5263                 if (readExactly(instream, buffer, 0, needed) == needed) {
5264                     mBytes += needed;
5265                 } else throw new IOException("Unexpected EOF in padding");
5266             }
5267         }
5268 
5269         // Read a widget metadata file, returning the restored blob
readMetadata(FileMetadata info, InputStream instream)5270         void readMetadata(FileMetadata info, InputStream instream) throws IOException {
5271             // Fail on suspiciously large widget dump files
5272             if (info.size > 64 * 1024) {
5273                 throw new IOException("Metadata too big; corrupt? size=" + info.size);
5274             }
5275 
5276             byte[] buffer = new byte[(int) info.size];
5277             if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5278                 mBytes += info.size;
5279             } else throw new IOException("Unexpected EOF in widget data");
5280 
5281             String[] str = new String[1];
5282             int offset = extractLine(buffer, 0, str);
5283             int version = Integer.parseInt(str[0]);
5284             if (version == BACKUP_MANIFEST_VERSION) {
5285                 offset = extractLine(buffer, offset, str);
5286                 final String pkg = str[0];
5287                 if (info.packageName.equals(pkg)) {
5288                     // Data checks out -- the rest of the buffer is a concatenation of
5289                     // binary blobs as described in the comment at writeAppWidgetData()
5290                     ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
5291                             offset, buffer.length - offset);
5292                     DataInputStream in = new DataInputStream(bin);
5293                     while (bin.available() > 0) {
5294                         int token = in.readInt();
5295                         int size = in.readInt();
5296                         if (size > 64 * 1024) {
5297                             throw new IOException("Datum "
5298                                     + Integer.toHexString(token)
5299                                     + " too big; corrupt? size=" + info.size);
5300                         }
5301                         switch (token) {
5302                             case BACKUP_WIDGET_METADATA_TOKEN:
5303                             {
5304                                 if (MORE_DEBUG) {
5305                                     Slog.i(TAG, "Got widget metadata for " + info.packageName);
5306                                 }
5307                                 mWidgetData = new byte[size];
5308                                 in.read(mWidgetData);
5309                                 break;
5310                             }
5311                             default:
5312                             {
5313                                 if (DEBUG) {
5314                                     Slog.i(TAG, "Ignoring metadata blob "
5315                                             + Integer.toHexString(token)
5316                                             + " for " + info.packageName);
5317                                 }
5318                                 in.skipBytes(size);
5319                                 break;
5320                             }
5321                         }
5322                     }
5323                 } else {
5324                     Slog.w(TAG, "Metadata mismatch: package " + info.packageName
5325                             + " but widget data for " + pkg);
5326                 }
5327             } else {
5328                 Slog.w(TAG, "Unsupported metadata version " + version);
5329             }
5330         }
5331 
5332         // Returns a policy constant
readAppManifest(FileMetadata info, InputStream instream)5333         RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
5334                 throws IOException {
5335             // Fail on suspiciously large manifest files
5336             if (info.size > 64 * 1024) {
5337                 throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
5338             }
5339 
5340             byte[] buffer = new byte[(int) info.size];
5341             if (MORE_DEBUG) {
5342                 Slog.i(TAG, "   readAppManifest() looking for " + info.size + " bytes, "
5343                         + mBytes + " already consumed");
5344             }
5345             if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
5346                 mBytes += info.size;
5347             } else throw new IOException("Unexpected EOF in manifest");
5348 
5349             RestorePolicy policy = RestorePolicy.IGNORE;
5350             String[] str = new String[1];
5351             int offset = 0;
5352 
5353             try {
5354                 offset = extractLine(buffer, offset, str);
5355                 int version = Integer.parseInt(str[0]);
5356                 if (version == BACKUP_MANIFEST_VERSION) {
5357                     offset = extractLine(buffer, offset, str);
5358                     String manifestPackage = str[0];
5359                     // TODO: handle <original-package>
5360                     if (manifestPackage.equals(info.packageName)) {
5361                         offset = extractLine(buffer, offset, str);
5362                         version = Integer.parseInt(str[0]);  // app version
5363                         offset = extractLine(buffer, offset, str);
5364                         // This is the platform version, which we don't use, but we parse it
5365                         // as a safety against corruption in the manifest.
5366                         Integer.parseInt(str[0]);
5367                         offset = extractLine(buffer, offset, str);
5368                         info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
5369                         offset = extractLine(buffer, offset, str);
5370                         boolean hasApk = str[0].equals("1");
5371                         offset = extractLine(buffer, offset, str);
5372                         int numSigs = Integer.parseInt(str[0]);
5373                         if (numSigs > 0) {
5374                             Signature[] sigs = new Signature[numSigs];
5375                             for (int i = 0; i < numSigs; i++) {
5376                                 offset = extractLine(buffer, offset, str);
5377                                 sigs[i] = new Signature(str[0]);
5378                             }
5379                             mManifestSignatures.put(info.packageName, sigs);
5380 
5381                             // Okay, got the manifest info we need...
5382                             try {
5383                                 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
5384                                         info.packageName, PackageManager.GET_SIGNATURES);
5385                                 // Fall through to IGNORE if the app explicitly disallows backup
5386                                 final int flags = pkgInfo.applicationInfo.flags;
5387                                 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
5388                                     // Restore system-uid-space packages only if they have
5389                                     // defined a custom backup agent
5390                                     if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
5391                                             || (pkgInfo.applicationInfo.backupAgentName != null)) {
5392                                         // Verify signatures against any installed version; if they
5393                                         // don't match, then we fall though and ignore the data.  The
5394                                         // signatureMatch() method explicitly ignores the signature
5395                                         // check for packages installed on the system partition, because
5396                                         // such packages are signed with the platform cert instead of
5397                                         // the app developer's cert, so they're different on every
5398                                         // device.
5399                                         if (signaturesMatch(sigs, pkgInfo)) {
5400                                             if (pkgInfo.versionCode >= version) {
5401                                                 Slog.i(TAG, "Sig + version match; taking data");
5402                                                 policy = RestorePolicy.ACCEPT;
5403                                             } else {
5404                                                 // The data is from a newer version of the app than
5405                                                 // is presently installed.  That means we can only
5406                                                 // use it if the matching apk is also supplied.
5407                                                 if (mAllowApks) {
5408                                                     Slog.i(TAG, "Data version " + version
5409                                                             + " is newer than installed version "
5410                                                             + pkgInfo.versionCode
5411                                                             + " - requiring apk");
5412                                                     policy = RestorePolicy.ACCEPT_IF_APK;
5413                                                 } else {
5414                                                     Slog.i(TAG, "Data requires newer version "
5415                                                             + version + "; ignoring");
5416                                                     policy = RestorePolicy.IGNORE;
5417                                                 }
5418                                             }
5419                                         } else {
5420                                             Slog.w(TAG, "Restore manifest signatures do not match "
5421                                                     + "installed application for " + info.packageName);
5422                                         }
5423                                     } else {
5424                                         Slog.w(TAG, "Package " + info.packageName
5425                                                 + " is system level with no agent");
5426                                     }
5427                                 } else {
5428                                     if (DEBUG) Slog.i(TAG, "Restore manifest from "
5429                                             + info.packageName + " but allowBackup=false");
5430                                 }
5431                             } catch (NameNotFoundException e) {
5432                                 // Okay, the target app isn't installed.  We can process
5433                                 // the restore properly only if the dataset provides the
5434                                 // apk file and we can successfully install it.
5435                                 if (mAllowApks) {
5436                                     if (DEBUG) Slog.i(TAG, "Package " + info.packageName
5437                                             + " not installed; requiring apk in dataset");
5438                                     policy = RestorePolicy.ACCEPT_IF_APK;
5439                                 } else {
5440                                     policy = RestorePolicy.IGNORE;
5441                                 }
5442                             }
5443 
5444                             if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
5445                                 Slog.i(TAG, "Cannot restore package " + info.packageName
5446                                         + " without the matching .apk");
5447                             }
5448                         } else {
5449                             Slog.i(TAG, "Missing signature on backed-up package "
5450                                     + info.packageName);
5451                         }
5452                     } else {
5453                         Slog.i(TAG, "Expected package " + info.packageName
5454                                 + " but restore manifest claims " + manifestPackage);
5455                     }
5456                 } else {
5457                     Slog.i(TAG, "Unknown restore manifest version " + version
5458                             + " for package " + info.packageName);
5459                 }
5460             } catch (NumberFormatException e) {
5461                 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
5462             } catch (IllegalArgumentException e) {
5463                 Slog.w(TAG, e.getMessage());
5464             }
5465 
5466             return policy;
5467         }
5468 
5469         // Builds a line from a byte buffer starting at 'offset', and returns
5470         // the index of the next unconsumed data in the buffer.
extractLine(byte[] buffer, int offset, String[] outStr)5471         int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
5472             final int end = buffer.length;
5473             if (offset >= end) throw new IOException("Incomplete data");
5474 
5475             int pos;
5476             for (pos = offset; pos < end; pos++) {
5477                 byte c = buffer[pos];
5478                 // at LF we declare end of line, and return the next char as the
5479                 // starting point for the next time through
5480                 if (c == '\n') {
5481                     break;
5482                 }
5483             }
5484             outStr[0] = new String(buffer, offset, pos - offset);
5485             pos++;  // may be pointing an extra byte past the end but that's okay
5486             return pos;
5487         }
5488 
dumpFileMetadata(FileMetadata info)5489         void dumpFileMetadata(FileMetadata info) {
5490             if (MORE_DEBUG) {
5491                 StringBuilder b = new StringBuilder(128);
5492 
5493                 // mode string
5494                 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
5495                 b.append(((info.mode & 0400) != 0) ? 'r' : '-');
5496                 b.append(((info.mode & 0200) != 0) ? 'w' : '-');
5497                 b.append(((info.mode & 0100) != 0) ? 'x' : '-');
5498                 b.append(((info.mode & 0040) != 0) ? 'r' : '-');
5499                 b.append(((info.mode & 0020) != 0) ? 'w' : '-');
5500                 b.append(((info.mode & 0010) != 0) ? 'x' : '-');
5501                 b.append(((info.mode & 0004) != 0) ? 'r' : '-');
5502                 b.append(((info.mode & 0002) != 0) ? 'w' : '-');
5503                 b.append(((info.mode & 0001) != 0) ? 'x' : '-');
5504                 b.append(String.format(" %9d ", info.size));
5505 
5506                 Date stamp = new Date(info.mtime);
5507                 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
5508 
5509                 b.append(info.packageName);
5510                 b.append(" :: ");
5511                 b.append(info.domain);
5512                 b.append(" :: ");
5513                 b.append(info.path);
5514 
5515                 Slog.i(TAG, b.toString());
5516             }
5517         }
5518 
5519         // Consume a tar file header block [sequence] and accumulate the relevant metadata
readTarHeaders(InputStream instream)5520         FileMetadata readTarHeaders(InputStream instream) throws IOException {
5521             byte[] block = new byte[512];
5522             FileMetadata info = null;
5523 
5524             boolean gotHeader = readTarHeader(instream, block);
5525             if (gotHeader) {
5526                 try {
5527                     // okay, presume we're okay, and extract the various metadata
5528                     info = new FileMetadata();
5529                     info.size = extractRadix(block, 124, 12, 8);
5530                     info.mtime = extractRadix(block, 136, 12, 8);
5531                     info.mode = extractRadix(block, 100, 8, 8);
5532 
5533                     info.path = extractString(block, 345, 155); // prefix
5534                     String path = extractString(block, 0, 100);
5535                     if (path.length() > 0) {
5536                         if (info.path.length() > 0) info.path += '/';
5537                         info.path += path;
5538                     }
5539 
5540                     // tar link indicator field: 1 byte at offset 156 in the header.
5541                     int typeChar = block[156];
5542                     if (typeChar == 'x') {
5543                         // pax extended header, so we need to read that
5544                         gotHeader = readPaxExtendedHeader(instream, info);
5545                         if (gotHeader) {
5546                             // and after a pax extended header comes another real header -- read
5547                             // that to find the real file type
5548                             gotHeader = readTarHeader(instream, block);
5549                         }
5550                         if (!gotHeader) throw new IOException("Bad or missing pax header");
5551 
5552                         typeChar = block[156];
5553                     }
5554 
5555                     switch (typeChar) {
5556                         case '0': info.type = BackupAgent.TYPE_FILE; break;
5557                         case '5': {
5558                             info.type = BackupAgent.TYPE_DIRECTORY;
5559                             if (info.size != 0) {
5560                                 Slog.w(TAG, "Directory entry with nonzero size in header");
5561                                 info.size = 0;
5562                             }
5563                             break;
5564                         }
5565                         case 0: {
5566                             // presume EOF
5567                             if (MORE_DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
5568                             return null;
5569                         }
5570                         default: {
5571                             Slog.e(TAG, "Unknown tar entity type: " + typeChar);
5572                             throw new IOException("Unknown entity type " + typeChar);
5573                         }
5574                     }
5575 
5576                     // Parse out the path
5577                     //
5578                     // first: apps/shared/unrecognized
5579                     if (FullBackup.SHARED_PREFIX.regionMatches(0,
5580                             info.path, 0, FullBackup.SHARED_PREFIX.length())) {
5581                         // File in shared storage.  !!! TODO: implement this.
5582                         info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
5583                         info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
5584                         info.domain = FullBackup.SHARED_STORAGE_TOKEN;
5585                         if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
5586                     } else if (FullBackup.APPS_PREFIX.regionMatches(0,
5587                             info.path, 0, FullBackup.APPS_PREFIX.length())) {
5588                         // App content!  Parse out the package name and domain
5589 
5590                         // strip the apps/ prefix
5591                         info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
5592 
5593                         // extract the package name
5594                         int slash = info.path.indexOf('/');
5595                         if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
5596                         info.packageName = info.path.substring(0, slash);
5597                         info.path = info.path.substring(slash+1);
5598 
5599                         // if it's a manifest or metadata payload we're done, otherwise parse
5600                         // out the domain into which the file will be restored
5601                         if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
5602                                 && !info.path.equals(BACKUP_METADATA_FILENAME)) {
5603                             slash = info.path.indexOf('/');
5604                             if (slash < 0) {
5605                                 throw new IOException("Illegal semantic path in non-manifest "
5606                                         + info.path);
5607                             }
5608                             info.domain = info.path.substring(0, slash);
5609                             info.path = info.path.substring(slash + 1);
5610                         }
5611                     }
5612                 } catch (IOException e) {
5613                     if (DEBUG) {
5614                         Slog.e(TAG, "Parse error in header: " + e.getMessage());
5615                         if (MORE_DEBUG) {
5616                             HEXLOG(block);
5617                         }
5618                     }
5619                     throw e;
5620                 }
5621             }
5622             return info;
5623         }
5624 
isRestorableFile(FileMetadata info)5625         private boolean isRestorableFile(FileMetadata info) {
5626             if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
5627                 if (MORE_DEBUG) {
5628                     Slog.i(TAG, "Dropping cache file path " + info.path);
5629                 }
5630                 return false;
5631             }
5632 
5633             if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
5634                 // It's possible this is "no-backup" dir contents in an archive stream
5635                 // produced on a device running a version of the OS that predates that
5636                 // API.  Respect the no-backup intention and don't let the data get to
5637                 // the app.
5638                 if (info.path.startsWith("no_backup/")) {
5639                     if (MORE_DEBUG) {
5640                         Slog.i(TAG, "Dropping no_backup file path " + info.path);
5641                     }
5642                     return false;
5643                 }
5644             }
5645 
5646             // The path needs to be canonical
5647             if (info.path.contains("..") || info.path.contains("//")) {
5648                 if (MORE_DEBUG) {
5649                     Slog.w(TAG, "Dropping invalid path " + info.path);
5650                 }
5651                 return false;
5652             }
5653 
5654             // Otherwise we think this file is good to go
5655             return true;
5656         }
5657 
HEXLOG(byte[] block)5658         private void HEXLOG(byte[] block) {
5659             int offset = 0;
5660             int todo = block.length;
5661             StringBuilder buf = new StringBuilder(64);
5662             while (todo > 0) {
5663                 buf.append(String.format("%04x   ", offset));
5664                 int numThisLine = (todo > 16) ? 16 : todo;
5665                 for (int i = 0; i < numThisLine; i++) {
5666                     buf.append(String.format("%02x ", block[offset+i]));
5667                 }
5668                 Slog.i("hexdump", buf.toString());
5669                 buf.setLength(0);
5670                 todo -= numThisLine;
5671                 offset += numThisLine;
5672             }
5673         }
5674 
5675         // Read exactly the given number of bytes into a buffer at the stated offset.
5676         // Returns false if EOF is encountered before the requested number of bytes
5677         // could be read.
readExactly(InputStream in, byte[] buffer, int offset, int size)5678         int readExactly(InputStream in, byte[] buffer, int offset, int size)
5679                 throws IOException {
5680             if (size <= 0) throw new IllegalArgumentException("size must be > 0");
5681 if (MORE_DEBUG) Slog.i(TAG, "  ... readExactly(" + size + ") called");
5682             int soFar = 0;
5683             while (soFar < size) {
5684                 int nRead = in.read(buffer, offset + soFar, size - soFar);
5685                 if (nRead <= 0) {
5686                     if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
5687                     break;
5688                 }
5689                 soFar += nRead;
5690 if (MORE_DEBUG) Slog.v(TAG, "   + got " + nRead + "; now wanting " + (size - soFar));
5691             }
5692             return soFar;
5693         }
5694 
readTarHeader(InputStream instream, byte[] block)5695         boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
5696             final int got = readExactly(instream, block, 0, 512);
5697             if (got == 0) return false;     // Clean EOF
5698             if (got < 512) throw new IOException("Unable to read full block header");
5699             mBytes += 512;
5700             return true;
5701         }
5702 
5703         // overwrites 'info' fields based on the pax extended header
readPaxExtendedHeader(InputStream instream, FileMetadata info)5704         boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
5705                 throws IOException {
5706             // We should never see a pax extended header larger than this
5707             if (info.size > 32*1024) {
5708                 Slog.w(TAG, "Suspiciously large pax header size " + info.size
5709                         + " - aborting");
5710                 throw new IOException("Sanity failure: pax header size " + info.size);
5711             }
5712 
5713             // read whole blocks, not just the content size
5714             int numBlocks = (int)((info.size + 511) >> 9);
5715             byte[] data = new byte[numBlocks * 512];
5716             if (readExactly(instream, data, 0, data.length) < data.length) {
5717                 throw new IOException("Unable to read full pax header");
5718             }
5719             mBytes += data.length;
5720 
5721             final int contentSize = (int) info.size;
5722             int offset = 0;
5723             do {
5724                 // extract the line at 'offset'
5725                 int eol = offset+1;
5726                 while (eol < contentSize && data[eol] != ' ') eol++;
5727                 if (eol >= contentSize) {
5728                     // error: we just hit EOD looking for the end of the size field
5729                     throw new IOException("Invalid pax data");
5730                 }
5731                 // eol points to the space between the count and the key
5732                 int linelen = (int) extractRadix(data, offset, eol - offset, 10);
5733                 int key = eol + 1;  // start of key=value
5734                 eol = offset + linelen - 1; // trailing LF
5735                 int value;
5736                 for (value = key+1; data[value] != '=' && value <= eol; value++);
5737                 if (value > eol) {
5738                     throw new IOException("Invalid pax declaration");
5739                 }
5740 
5741                 // pax requires that key/value strings be in UTF-8
5742                 String keyStr = new String(data, key, value-key, "UTF-8");
5743                 // -1 to strip the trailing LF
5744                 String valStr = new String(data, value+1, eol-value-1, "UTF-8");
5745 
5746                 if ("path".equals(keyStr)) {
5747                     info.path = valStr;
5748                 } else if ("size".equals(keyStr)) {
5749                     info.size = Long.parseLong(valStr);
5750                 } else {
5751                     if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
5752                 }
5753 
5754                 offset += linelen;
5755             } while (offset < contentSize);
5756 
5757             return true;
5758         }
5759 
extractRadix(byte[] data, int offset, int maxChars, int radix)5760         long extractRadix(byte[] data, int offset, int maxChars, int radix)
5761                 throws IOException {
5762             long value = 0;
5763             final int end = offset + maxChars;
5764             for (int i = offset; i < end; i++) {
5765                 final byte b = data[i];
5766                 // Numeric fields in tar can terminate with either NUL or SPC
5767                 if (b == 0 || b == ' ') break;
5768                 if (b < '0' || b > ('0' + radix - 1)) {
5769                     throw new IOException("Invalid number in header: '" + (char)b
5770                             + "' for radix " + radix);
5771                 }
5772                 value = radix * value + (b - '0');
5773             }
5774             return value;
5775         }
5776 
extractString(byte[] data, int offset, int maxChars)5777         String extractString(byte[] data, int offset, int maxChars) throws IOException {
5778             final int end = offset + maxChars;
5779             int eos = offset;
5780             // tar string fields terminate early with a NUL
5781             while (eos < end && data[eos] != 0) eos++;
5782             return new String(data, offset, eos-offset, "US-ASCII");
5783         }
5784 
sendStartRestore()5785         void sendStartRestore() {
5786             if (mObserver != null) {
5787                 try {
5788                     mObserver.onStartRestore();
5789                 } catch (RemoteException e) {
5790                     Slog.w(TAG, "full restore observer went away: startRestore");
5791                     mObserver = null;
5792                 }
5793             }
5794         }
5795 
sendOnRestorePackage(String name)5796         void sendOnRestorePackage(String name) {
5797             if (mObserver != null) {
5798                 try {
5799                     // TODO: use a more user-friendly name string
5800                     mObserver.onRestorePackage(name);
5801                 } catch (RemoteException e) {
5802                     Slog.w(TAG, "full restore observer went away: restorePackage");
5803                     mObserver = null;
5804                 }
5805             }
5806         }
5807 
sendEndRestore()5808         void sendEndRestore() {
5809             if (mObserver != null) {
5810                 try {
5811                     mObserver.onEndRestore();
5812                 } catch (RemoteException e) {
5813                     Slog.w(TAG, "full restore observer went away: endRestore");
5814                     mObserver = null;
5815                 }
5816             }
5817         }
5818     }
5819 
5820     // ***** end new engine class ***
5821 
5822     class PerformAdbRestoreTask implements Runnable {
5823         ParcelFileDescriptor mInputFile;
5824         String mCurrentPassword;
5825         String mDecryptPassword;
5826         IFullBackupRestoreObserver mObserver;
5827         AtomicBoolean mLatchObject;
5828         IBackupAgent mAgent;
5829         String mAgentPackage;
5830         ApplicationInfo mTargetApp;
5831         FullBackupObbConnection mObbConnection = null;
5832         ParcelFileDescriptor[] mPipes = null;
5833         byte[] mWidgetData = null;
5834 
5835         long mBytes;
5836 
5837         // possible handling states for a given package in the restore dataset
5838         final HashMap<String, RestorePolicy> mPackagePolicies
5839                 = new HashMap<String, RestorePolicy>();
5840 
5841         // installer package names for each encountered app, derived from the manifests
5842         final HashMap<String, String> mPackageInstallers = new HashMap<String, String>();
5843 
5844         // Signatures for a given package found in its manifest file
5845         final HashMap<String, Signature[]> mManifestSignatures
5846                 = new HashMap<String, Signature[]>();
5847 
5848         // Packages we've already wiped data on when restoring their first file
5849         final HashSet<String> mClearedPackages = new HashSet<String>();
5850 
PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword, IFullBackupRestoreObserver observer, AtomicBoolean latch)5851         PerformAdbRestoreTask(ParcelFileDescriptor fd, String curPassword, String decryptPassword,
5852                 IFullBackupRestoreObserver observer, AtomicBoolean latch) {
5853             mInputFile = fd;
5854             mCurrentPassword = curPassword;
5855             mDecryptPassword = decryptPassword;
5856             mObserver = observer;
5857             mLatchObject = latch;
5858             mAgent = null;
5859             mAgentPackage = null;
5860             mTargetApp = null;
5861             mObbConnection = new FullBackupObbConnection();
5862 
5863             // Which packages we've already wiped data on.  We prepopulate this
5864             // with a whitelist of packages known to be unclearable.
5865             mClearedPackages.add("android");
5866             mClearedPackages.add(SETTINGS_PACKAGE);
5867         }
5868 
5869         class RestoreFileRunnable implements Runnable {
5870             IBackupAgent mAgent;
5871             FileMetadata mInfo;
5872             ParcelFileDescriptor mSocket;
5873             int mToken;
5874 
RestoreFileRunnable(IBackupAgent agent, FileMetadata info, ParcelFileDescriptor socket, int token)5875             RestoreFileRunnable(IBackupAgent agent, FileMetadata info,
5876                     ParcelFileDescriptor socket, int token) throws IOException {
5877                 mAgent = agent;
5878                 mInfo = info;
5879                 mToken = token;
5880 
5881                 // This class is used strictly for process-local binder invocations.  The
5882                 // semantics of ParcelFileDescriptor differ in this case; in particular, we
5883                 // do not automatically get a 'dup'ed descriptor that we can can continue
5884                 // to use asynchronously from the caller.  So, we make sure to dup it ourselves
5885                 // before proceeding to do the restore.
5886                 mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
5887             }
5888 
5889             @Override
run()5890             public void run() {
5891                 try {
5892                     mAgent.doRestoreFile(mSocket, mInfo.size, mInfo.type,
5893                             mInfo.domain, mInfo.path, mInfo.mode, mInfo.mtime,
5894                             mToken, mBackupManagerBinder);
5895                 } catch (RemoteException e) {
5896                     // never happens; this is used strictly for local binder calls
5897                 }
5898             }
5899         }
5900 
5901         @Override
run()5902         public void run() {
5903             Slog.i(TAG, "--- Performing full-dataset restore ---");
5904             mObbConnection.establish();
5905             sendStartRestore();
5906 
5907             // Are we able to restore shared-storage data?
5908             if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
5909                 mPackagePolicies.put(SHARED_BACKUP_AGENT_PACKAGE, RestorePolicy.ACCEPT);
5910             }
5911 
5912             FileInputStream rawInStream = null;
5913             DataInputStream rawDataIn = null;
5914             try {
5915                 if (!backupPasswordMatches(mCurrentPassword)) {
5916                     if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
5917                     return;
5918                 }
5919 
5920                 mBytes = 0;
5921                 byte[] buffer = new byte[32 * 1024];
5922                 rawInStream = new FileInputStream(mInputFile.getFileDescriptor());
5923                 rawDataIn = new DataInputStream(rawInStream);
5924 
5925                 // First, parse out the unencrypted/uncompressed header
5926                 boolean compressed = false;
5927                 InputStream preCompressStream = rawInStream;
5928                 final InputStream in;
5929 
5930                 boolean okay = false;
5931                 final int headerLen = BACKUP_FILE_HEADER_MAGIC.length();
5932                 byte[] streamHeader = new byte[headerLen];
5933                 rawDataIn.readFully(streamHeader);
5934                 byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes("UTF-8");
5935                 if (Arrays.equals(magicBytes, streamHeader)) {
5936                     // okay, header looks good.  now parse out the rest of the fields.
5937                     String s = readHeaderLine(rawInStream);
5938                     final int archiveVersion = Integer.parseInt(s);
5939                     if (archiveVersion <= BACKUP_FILE_VERSION) {
5940                         // okay, it's a version we recognize.  if it's version 1, we may need
5941                         // to try two different PBKDF2 regimes to compare checksums.
5942                         final boolean pbkdf2Fallback = (archiveVersion == 1);
5943 
5944                         s = readHeaderLine(rawInStream);
5945                         compressed = (Integer.parseInt(s) != 0);
5946                         s = readHeaderLine(rawInStream);
5947                         if (s.equals("none")) {
5948                             // no more header to parse; we're good to go
5949                             okay = true;
5950                         } else if (mDecryptPassword != null && mDecryptPassword.length() > 0) {
5951                             preCompressStream = decodeAesHeaderAndInitialize(s, pbkdf2Fallback,
5952                                     rawInStream);
5953                             if (preCompressStream != null) {
5954                                 okay = true;
5955                             }
5956                         } else Slog.w(TAG, "Archive is encrypted but no password given");
5957                     } else Slog.w(TAG, "Wrong header version: " + s);
5958                 } else Slog.w(TAG, "Didn't read the right header magic");
5959 
5960                 if (!okay) {
5961                     Slog.w(TAG, "Invalid restore data; aborting.");
5962                     return;
5963                 }
5964 
5965                 // okay, use the right stream layer based on compression
5966                 in = (compressed) ? new InflaterInputStream(preCompressStream) : preCompressStream;
5967 
5968                 boolean didRestore;
5969                 do {
5970                     didRestore = restoreOneFile(in, buffer);
5971                 } while (didRestore);
5972 
5973                 if (MORE_DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
5974             } catch (IOException e) {
5975                 Slog.e(TAG, "Unable to read restore input");
5976             } finally {
5977                 tearDownPipes();
5978                 tearDownAgent(mTargetApp);
5979 
5980                 try {
5981                     if (rawDataIn != null) rawDataIn.close();
5982                     if (rawInStream != null) rawInStream.close();
5983                     mInputFile.close();
5984                 } catch (IOException e) {
5985                     Slog.w(TAG, "Close of restore data pipe threw", e);
5986                     /* nothing we can do about this */
5987                 }
5988                 synchronized (mCurrentOpLock) {
5989                     mCurrentOperations.clear();
5990                 }
5991                 synchronized (mLatchObject) {
5992                     mLatchObject.set(true);
5993                     mLatchObject.notifyAll();
5994                 }
5995                 mObbConnection.tearDown();
5996                 sendEndRestore();
5997                 Slog.d(TAG, "Full restore pass complete.");
5998                 mWakelock.release();
5999             }
6000         }
6001 
readHeaderLine(InputStream in)6002         String readHeaderLine(InputStream in) throws IOException {
6003             int c;
6004             StringBuilder buffer = new StringBuilder(80);
6005             while ((c = in.read()) >= 0) {
6006                 if (c == '\n') break;   // consume and discard the newlines
6007                 buffer.append((char)c);
6008             }
6009             return buffer.toString();
6010         }
6011 
attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt, int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream, boolean doLog)6012         InputStream attemptMasterKeyDecryption(String algorithm, byte[] userSalt, byte[] ckSalt,
6013                 int rounds, String userIvHex, String masterKeyBlobHex, InputStream rawInStream,
6014                 boolean doLog) {
6015             InputStream result = null;
6016 
6017             try {
6018                 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
6019                 SecretKey userKey = buildPasswordKey(algorithm, mDecryptPassword, userSalt,
6020                         rounds);
6021                 byte[] IV = hexToByteArray(userIvHex);
6022                 IvParameterSpec ivSpec = new IvParameterSpec(IV);
6023                 c.init(Cipher.DECRYPT_MODE,
6024                         new SecretKeySpec(userKey.getEncoded(), "AES"),
6025                         ivSpec);
6026                 byte[] mkCipher = hexToByteArray(masterKeyBlobHex);
6027                 byte[] mkBlob = c.doFinal(mkCipher);
6028 
6029                 // first, the master key IV
6030                 int offset = 0;
6031                 int len = mkBlob[offset++];
6032                 IV = Arrays.copyOfRange(mkBlob, offset, offset + len);
6033                 offset += len;
6034                 // then the master key itself
6035                 len = mkBlob[offset++];
6036                 byte[] mk = Arrays.copyOfRange(mkBlob,
6037                         offset, offset + len);
6038                 offset += len;
6039                 // and finally the master key checksum hash
6040                 len = mkBlob[offset++];
6041                 byte[] mkChecksum = Arrays.copyOfRange(mkBlob,
6042                         offset, offset + len);
6043 
6044                 // now validate the decrypted master key against the checksum
6045                 byte[] calculatedCk = makeKeyChecksum(algorithm, mk, ckSalt, rounds);
6046                 if (Arrays.equals(calculatedCk, mkChecksum)) {
6047                     ivSpec = new IvParameterSpec(IV);
6048                     c.init(Cipher.DECRYPT_MODE,
6049                             new SecretKeySpec(mk, "AES"),
6050                             ivSpec);
6051                     // Only if all of the above worked properly will 'result' be assigned
6052                     result = new CipherInputStream(rawInStream, c);
6053                 } else if (doLog) Slog.w(TAG, "Incorrect password");
6054             } catch (InvalidAlgorithmParameterException e) {
6055                 if (doLog) Slog.e(TAG, "Needed parameter spec unavailable!", e);
6056             } catch (BadPaddingException e) {
6057                 // This case frequently occurs when the wrong password is used to decrypt
6058                 // the master key.  Use the identical "incorrect password" log text as is
6059                 // used in the checksum failure log in order to avoid providing additional
6060                 // information to an attacker.
6061                 if (doLog) Slog.w(TAG, "Incorrect password");
6062             } catch (IllegalBlockSizeException e) {
6063                 if (doLog) Slog.w(TAG, "Invalid block size in master key");
6064             } catch (NoSuchAlgorithmException e) {
6065                 if (doLog) Slog.e(TAG, "Needed decryption algorithm unavailable!");
6066             } catch (NoSuchPaddingException e) {
6067                 if (doLog) Slog.e(TAG, "Needed padding mechanism unavailable!");
6068             } catch (InvalidKeyException e) {
6069                 if (doLog) Slog.w(TAG, "Illegal password; aborting");
6070             }
6071 
6072             return result;
6073         }
6074 
decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback, InputStream rawInStream)6075         InputStream decodeAesHeaderAndInitialize(String encryptionName, boolean pbkdf2Fallback,
6076                 InputStream rawInStream) {
6077             InputStream result = null;
6078             try {
6079                 if (encryptionName.equals(ENCRYPTION_ALGORITHM_NAME)) {
6080 
6081                     String userSaltHex = readHeaderLine(rawInStream); // 5
6082                     byte[] userSalt = hexToByteArray(userSaltHex);
6083 
6084                     String ckSaltHex = readHeaderLine(rawInStream); // 6
6085                     byte[] ckSalt = hexToByteArray(ckSaltHex);
6086 
6087                     int rounds = Integer.parseInt(readHeaderLine(rawInStream)); // 7
6088                     String userIvHex = readHeaderLine(rawInStream); // 8
6089 
6090                     String masterKeyBlobHex = readHeaderLine(rawInStream); // 9
6091 
6092                     // decrypt the master key blob
6093                     result = attemptMasterKeyDecryption(PBKDF_CURRENT, userSalt, ckSalt,
6094                             rounds, userIvHex, masterKeyBlobHex, rawInStream, false);
6095                     if (result == null && pbkdf2Fallback) {
6096                         result = attemptMasterKeyDecryption(PBKDF_FALLBACK, userSalt, ckSalt,
6097                                 rounds, userIvHex, masterKeyBlobHex, rawInStream, true);
6098                     }
6099                 } else Slog.w(TAG, "Unsupported encryption method: " + encryptionName);
6100             } catch (NumberFormatException e) {
6101                 Slog.w(TAG, "Can't parse restore data header");
6102             } catch (IOException e) {
6103                 Slog.w(TAG, "Can't read input header");
6104             }
6105 
6106             return result;
6107         }
6108 
restoreOneFile(InputStream instream, byte[] buffer)6109         boolean restoreOneFile(InputStream instream, byte[] buffer) {
6110             FileMetadata info;
6111             try {
6112                 info = readTarHeaders(instream);
6113                 if (info != null) {
6114                     if (MORE_DEBUG) {
6115                         dumpFileMetadata(info);
6116                     }
6117 
6118                     final String pkg = info.packageName;
6119                     if (!pkg.equals(mAgentPackage)) {
6120                         // okay, change in package; set up our various
6121                         // bookkeeping if we haven't seen it yet
6122                         if (!mPackagePolicies.containsKey(pkg)) {
6123                             mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6124                         }
6125 
6126                         // Clean up the previous agent relationship if necessary,
6127                         // and let the observer know we're considering a new app.
6128                         if (mAgent != null) {
6129                             if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
6130                             // Now we're really done
6131                             tearDownPipes();
6132                             tearDownAgent(mTargetApp);
6133                             mTargetApp = null;
6134                             mAgentPackage = null;
6135                         }
6136                     }
6137 
6138                     if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
6139                         mPackagePolicies.put(pkg, readAppManifest(info, instream));
6140                         mPackageInstallers.put(pkg, info.installerPackageName);
6141                         // We've read only the manifest content itself at this point,
6142                         // so consume the footer before looping around to the next
6143                         // input file
6144                         skipTarPadding(info.size, instream);
6145                         sendOnRestorePackage(pkg);
6146                     } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
6147                         // Metadata blobs!
6148                         readMetadata(info, instream);
6149                         skipTarPadding(info.size, instream);
6150                     } else {
6151                         // Non-manifest, so it's actual file data.  Is this a package
6152                         // we're ignoring?
6153                         boolean okay = true;
6154                         RestorePolicy policy = mPackagePolicies.get(pkg);
6155                         switch (policy) {
6156                             case IGNORE:
6157                                 okay = false;
6158                                 break;
6159 
6160                             case ACCEPT_IF_APK:
6161                                 // If we're in accept-if-apk state, then the first file we
6162                                 // see MUST be the apk.
6163                                 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6164                                     if (DEBUG) Slog.d(TAG, "APK file; installing");
6165                                     // Try to install the app.
6166                                     String installerName = mPackageInstallers.get(pkg);
6167                                     okay = installApk(info, installerName, instream);
6168                                     // good to go; promote to ACCEPT
6169                                     mPackagePolicies.put(pkg, (okay)
6170                                             ? RestorePolicy.ACCEPT
6171                                             : RestorePolicy.IGNORE);
6172                                     // At this point we've consumed this file entry
6173                                     // ourselves, so just strip the tar footer and
6174                                     // go on to the next file in the input stream
6175                                     skipTarPadding(info.size, instream);
6176                                     return true;
6177                                 } else {
6178                                     // File data before (or without) the apk.  We can't
6179                                     // handle it coherently in this case so ignore it.
6180                                     mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6181                                     okay = false;
6182                                 }
6183                                 break;
6184 
6185                             case ACCEPT:
6186                                 if (info.domain.equals(FullBackup.APK_TREE_TOKEN)) {
6187                                     if (DEBUG) Slog.d(TAG, "apk present but ACCEPT");
6188                                     // we can take the data without the apk, so we
6189                                     // *want* to do so.  skip the apk by declaring this
6190                                     // one file not-okay without changing the restore
6191                                     // policy for the package.
6192                                     okay = false;
6193                                 }
6194                                 break;
6195 
6196                             default:
6197                                 // Something has gone dreadfully wrong when determining
6198                                 // the restore policy from the manifest.  Ignore the
6199                                 // rest of this package's data.
6200                                 Slog.e(TAG, "Invalid policy from manifest");
6201                                 okay = false;
6202                                 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6203                                 break;
6204                         }
6205 
6206                         // The path needs to be canonical
6207                         if (info.path.contains("..") || info.path.contains("//")) {
6208                             if (MORE_DEBUG) {
6209                                 Slog.w(TAG, "Dropping invalid path " + info.path);
6210                             }
6211                             okay = false;
6212                         }
6213 
6214                         // If the policy is satisfied, go ahead and set up to pipe the
6215                         // data to the agent.
6216                         if (DEBUG && okay && mAgent != null) {
6217                             Slog.i(TAG, "Reusing existing agent instance");
6218                         }
6219                         if (okay && mAgent == null) {
6220                             if (DEBUG) Slog.d(TAG, "Need to launch agent for " + pkg);
6221 
6222                             try {
6223                                 mTargetApp = mPackageManager.getApplicationInfo(pkg, 0);
6224 
6225                                 // If we haven't sent any data to this app yet, we probably
6226                                 // need to clear it first.  Check that.
6227                                 if (!mClearedPackages.contains(pkg)) {
6228                                     // apps with their own backup agents are
6229                                     // responsible for coherently managing a full
6230                                     // restore.
6231                                     if (mTargetApp.backupAgentName == null) {
6232                                         if (DEBUG) Slog.d(TAG, "Clearing app data preparatory to full restore");
6233                                         clearApplicationDataSynchronous(pkg);
6234                                     } else {
6235                                         if (DEBUG) Slog.d(TAG, "backup agent ("
6236                                                 + mTargetApp.backupAgentName + ") => no clear");
6237                                     }
6238                                     mClearedPackages.add(pkg);
6239                                 } else {
6240                                     if (DEBUG) Slog.d(TAG, "We've initialized this app already; no clear required");
6241                                 }
6242 
6243                                 // All set; now set up the IPC and launch the agent
6244                                 setUpPipes();
6245                                 mAgent = bindToAgentSynchronous(mTargetApp,
6246                                         IApplicationThread.BACKUP_MODE_RESTORE_FULL);
6247                                 mAgentPackage = pkg;
6248                             } catch (IOException e) {
6249                                 // fall through to error handling
6250                             } catch (NameNotFoundException e) {
6251                                 // fall through to error handling
6252                             }
6253 
6254                             if (mAgent == null) {
6255                                 if (DEBUG) Slog.d(TAG, "Unable to create agent for " + pkg);
6256                                 okay = false;
6257                                 tearDownPipes();
6258                                 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6259                             }
6260                         }
6261 
6262                         // Sanity check: make sure we never give data to the wrong app.  This
6263                         // should never happen but a little paranoia here won't go amiss.
6264                         if (okay && !pkg.equals(mAgentPackage)) {
6265                             Slog.e(TAG, "Restoring data for " + pkg
6266                                     + " but agent is for " + mAgentPackage);
6267                             okay = false;
6268                         }
6269 
6270                         // At this point we have an agent ready to handle the full
6271                         // restore data as well as a pipe for sending data to
6272                         // that agent.  Tell the agent to start reading from the
6273                         // pipe.
6274                         if (okay) {
6275                             boolean agentSuccess = true;
6276                             long toCopy = info.size;
6277                             final int token = generateToken();
6278                             try {
6279                                 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
6280                                 if (info.domain.equals(FullBackup.OBB_TREE_TOKEN)) {
6281                                     if (DEBUG) Slog.d(TAG, "Restoring OBB file for " + pkg
6282                                             + " : " + info.path);
6283                                     mObbConnection.restoreObbFile(pkg, mPipes[0],
6284                                             info.size, info.type, info.path, info.mode,
6285                                             info.mtime, token, mBackupManagerBinder);
6286                                 } else {
6287                                     if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
6288                                             + info.path);
6289                                     // fire up the app's agent listening on the socket.  If
6290                                     // the agent is running in the system process we can't
6291                                     // just invoke it asynchronously, so we provide a thread
6292                                     // for it here.
6293                                     if (mTargetApp.processName.equals("system")) {
6294                                         Slog.d(TAG, "system process agent - spinning a thread");
6295                                         RestoreFileRunnable runner = new RestoreFileRunnable(
6296                                                 mAgent, info, mPipes[0], token);
6297                                         new Thread(runner, "restore-sys-runner").start();
6298                                     } else {
6299                                         mAgent.doRestoreFile(mPipes[0], info.size, info.type,
6300                                                 info.domain, info.path, info.mode, info.mtime,
6301                                                 token, mBackupManagerBinder);
6302                                     }
6303                                 }
6304                             } catch (IOException e) {
6305                                 // couldn't dup the socket for a process-local restore
6306                                 Slog.d(TAG, "Couldn't establish restore");
6307                                 agentSuccess = false;
6308                                 okay = false;
6309                             } catch (RemoteException e) {
6310                                 // whoops, remote entity went away.  We'll eat the content
6311                                 // ourselves, then, and not copy it over.
6312                                 Slog.e(TAG, "Agent crashed during full restore");
6313                                 agentSuccess = false;
6314                                 okay = false;
6315                             }
6316 
6317                             // Copy over the data if the agent is still good
6318                             if (okay) {
6319                                 boolean pipeOkay = true;
6320                                 FileOutputStream pipe = new FileOutputStream(
6321                                         mPipes[1].getFileDescriptor());
6322                                 while (toCopy > 0) {
6323                                     int toRead = (toCopy > buffer.length)
6324                                     ? buffer.length : (int)toCopy;
6325                                     int nRead = instream.read(buffer, 0, toRead);
6326                                     if (nRead >= 0) mBytes += nRead;
6327                                     if (nRead <= 0) break;
6328                                     toCopy -= nRead;
6329 
6330                                     // send it to the output pipe as long as things
6331                                     // are still good
6332                                     if (pipeOkay) {
6333                                         try {
6334                                             pipe.write(buffer, 0, nRead);
6335                                         } catch (IOException e) {
6336                                             Slog.e(TAG, "Failed to write to restore pipe", e);
6337                                             pipeOkay = false;
6338                                         }
6339                                     }
6340                                 }
6341 
6342                                 // done sending that file!  Now we just need to consume
6343                                 // the delta from info.size to the end of block.
6344                                 skipTarPadding(info.size, instream);
6345 
6346                                 // and now that we've sent it all, wait for the remote
6347                                 // side to acknowledge receipt
6348                                 agentSuccess = waitUntilOperationComplete(token);
6349                             }
6350 
6351                             // okay, if the remote end failed at any point, deal with
6352                             // it by ignoring the rest of the restore on it
6353                             if (!agentSuccess) {
6354                                 mBackupHandler.removeMessages(MSG_TIMEOUT);
6355                                 tearDownPipes();
6356                                 tearDownAgent(mTargetApp);
6357                                 mAgent = null;
6358                                 mPackagePolicies.put(pkg, RestorePolicy.IGNORE);
6359                             }
6360                         }
6361 
6362                         // Problems setting up the agent communication, or an already-
6363                         // ignored package: skip to the next tar stream entry by
6364                         // reading and discarding this file.
6365                         if (!okay) {
6366                             if (DEBUG) Slog.d(TAG, "[discarding file content]");
6367                             long bytesToConsume = (info.size + 511) & ~511;
6368                             while (bytesToConsume > 0) {
6369                                 int toRead = (bytesToConsume > buffer.length)
6370                                 ? buffer.length : (int)bytesToConsume;
6371                                 long nRead = instream.read(buffer, 0, toRead);
6372                                 if (nRead >= 0) mBytes += nRead;
6373                                 if (nRead <= 0) break;
6374                                 bytesToConsume -= nRead;
6375                             }
6376                         }
6377                     }
6378                 }
6379             } catch (IOException e) {
6380                 if (DEBUG) Slog.w(TAG, "io exception on restore socket read", e);
6381                 // treat as EOF
6382                 info = null;
6383             }
6384 
6385             return (info != null);
6386         }
6387 
setUpPipes()6388         void setUpPipes() throws IOException {
6389             mPipes = ParcelFileDescriptor.createPipe();
6390         }
6391 
tearDownPipes()6392         void tearDownPipes() {
6393             if (mPipes != null) {
6394                 try {
6395                     mPipes[0].close();
6396                     mPipes[0] = null;
6397                     mPipes[1].close();
6398                     mPipes[1] = null;
6399                 } catch (IOException e) {
6400                     Slog.w(TAG, "Couldn't close agent pipes", e);
6401                 }
6402                 mPipes = null;
6403             }
6404         }
6405 
tearDownAgent(ApplicationInfo app)6406         void tearDownAgent(ApplicationInfo app) {
6407             if (mAgent != null) {
6408                 try {
6409                     // unbind and tidy up even on timeout or failure, just in case
6410                     mActivityManager.unbindBackupAgent(app);
6411 
6412                     // The agent was running with a stub Application object, so shut it down.
6413                     // !!! We hardcode the confirmation UI's package name here rather than use a
6414                     //     manifest flag!  TODO something less direct.
6415                     if (app.uid != Process.SYSTEM_UID
6416                             && !app.packageName.equals("com.android.backupconfirm")) {
6417                         if (DEBUG) Slog.d(TAG, "Killing host process");
6418                         mActivityManager.killApplicationProcess(app.processName, app.uid);
6419                     } else {
6420                         if (DEBUG) Slog.d(TAG, "Not killing after full restore");
6421                     }
6422                 } catch (RemoteException e) {
6423                     Slog.d(TAG, "Lost app trying to shut down");
6424                 }
6425                 mAgent = null;
6426             }
6427         }
6428 
6429         class RestoreInstallObserver extends PackageInstallObserver {
6430             final AtomicBoolean mDone = new AtomicBoolean();
6431             String mPackageName;
6432             int mResult;
6433 
reset()6434             public void reset() {
6435                 synchronized (mDone) {
6436                     mDone.set(false);
6437                 }
6438             }
6439 
waitForCompletion()6440             public void waitForCompletion() {
6441                 synchronized (mDone) {
6442                     while (mDone.get() == false) {
6443                         try {
6444                             mDone.wait();
6445                         } catch (InterruptedException e) { }
6446                     }
6447                 }
6448             }
6449 
getResult()6450             int getResult() {
6451                 return mResult;
6452             }
6453 
6454             @Override
onPackageInstalled(String packageName, int returnCode, String msg, Bundle extras)6455             public void onPackageInstalled(String packageName, int returnCode,
6456                     String msg, Bundle extras) {
6457                 synchronized (mDone) {
6458                     mResult = returnCode;
6459                     mPackageName = packageName;
6460                     mDone.set(true);
6461                     mDone.notifyAll();
6462                 }
6463             }
6464         }
6465 
6466         class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
6467             final AtomicBoolean mDone = new AtomicBoolean();
6468             int mResult;
6469 
reset()6470             public void reset() {
6471                 synchronized (mDone) {
6472                     mDone.set(false);
6473                 }
6474             }
6475 
waitForCompletion()6476             public void waitForCompletion() {
6477                 synchronized (mDone) {
6478                     while (mDone.get() == false) {
6479                         try {
6480                             mDone.wait();
6481                         } catch (InterruptedException e) { }
6482                     }
6483                 }
6484             }
6485 
6486             @Override
packageDeleted(String packageName, int returnCode)6487             public void packageDeleted(String packageName, int returnCode) throws RemoteException {
6488                 synchronized (mDone) {
6489                     mResult = returnCode;
6490                     mDone.set(true);
6491                     mDone.notifyAll();
6492                 }
6493             }
6494         }
6495 
6496         final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
6497         final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
6498 
installApk(FileMetadata info, String installerPackage, InputStream instream)6499         boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
6500             boolean okay = true;
6501 
6502             if (DEBUG) Slog.d(TAG, "Installing from backup: " + info.packageName);
6503 
6504             // The file content is an .apk file.  Copy it out to a staging location and
6505             // attempt to install it.
6506             File apkFile = new File(mDataDir, info.packageName);
6507             try {
6508                 FileOutputStream apkStream = new FileOutputStream(apkFile);
6509                 byte[] buffer = new byte[32 * 1024];
6510                 long size = info.size;
6511                 while (size > 0) {
6512                     long toRead = (buffer.length < size) ? buffer.length : size;
6513                     int didRead = instream.read(buffer, 0, (int)toRead);
6514                     if (didRead >= 0) mBytes += didRead;
6515                     apkStream.write(buffer, 0, didRead);
6516                     size -= didRead;
6517                 }
6518                 apkStream.close();
6519 
6520                 // make sure the installer can read it
6521                 apkFile.setReadable(true, false);
6522 
6523                 // Now install it
6524                 Uri packageUri = Uri.fromFile(apkFile);
6525                 mInstallObserver.reset();
6526                 mPackageManager.installPackage(packageUri, mInstallObserver,
6527                         PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
6528                         installerPackage);
6529                 mInstallObserver.waitForCompletion();
6530 
6531                 if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
6532                     // The only time we continue to accept install of data even if the
6533                     // apk install failed is if we had already determined that we could
6534                     // accept the data regardless.
6535                     if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
6536                         okay = false;
6537                     }
6538                 } else {
6539                     // Okay, the install succeeded.  Make sure it was the right app.
6540                     boolean uninstall = false;
6541                     if (!mInstallObserver.mPackageName.equals(info.packageName)) {
6542                         Slog.w(TAG, "Restore stream claimed to include apk for "
6543                                 + info.packageName + " but apk was really "
6544                                 + mInstallObserver.mPackageName);
6545                         // delete the package we just put in place; it might be fraudulent
6546                         okay = false;
6547                         uninstall = true;
6548                     } else {
6549                         try {
6550                             PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
6551                                     PackageManager.GET_SIGNATURES);
6552                             if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
6553                                 Slog.w(TAG, "Restore stream contains apk of package "
6554                                         + info.packageName + " but it disallows backup/restore");
6555                                 okay = false;
6556                             } else {
6557                                 // So far so good -- do the signatures match the manifest?
6558                                 Signature[] sigs = mManifestSignatures.get(info.packageName);
6559                                 if (signaturesMatch(sigs, pkg)) {
6560                                     // If this is a system-uid app without a declared backup agent,
6561                                     // don't restore any of the file data.
6562                                     if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
6563                                             && (pkg.applicationInfo.backupAgentName == null)) {
6564                                         Slog.w(TAG, "Installed app " + info.packageName
6565                                                 + " has restricted uid and no agent");
6566                                         okay = false;
6567                                     }
6568                                 } else {
6569                                     Slog.w(TAG, "Installed app " + info.packageName
6570                                             + " signatures do not match restore manifest");
6571                                     okay = false;
6572                                     uninstall = true;
6573                                 }
6574                             }
6575                         } catch (NameNotFoundException e) {
6576                             Slog.w(TAG, "Install of package " + info.packageName
6577                                     + " succeeded but now not found");
6578                             okay = false;
6579                         }
6580                     }
6581 
6582                     // If we're not okay at this point, we need to delete the package
6583                     // that we just installed.
6584                     if (uninstall) {
6585                         mDeleteObserver.reset();
6586                         mPackageManager.deletePackage(mInstallObserver.mPackageName,
6587                                 mDeleteObserver, 0);
6588                         mDeleteObserver.waitForCompletion();
6589                     }
6590                 }
6591             } catch (IOException e) {
6592                 Slog.e(TAG, "Unable to transcribe restored apk for install");
6593                 okay = false;
6594             } finally {
6595                 apkFile.delete();
6596             }
6597 
6598             return okay;
6599         }
6600 
6601         // Given an actual file content size, consume the post-content padding mandated
6602         // by the tar format.
skipTarPadding(long size, InputStream instream)6603         void skipTarPadding(long size, InputStream instream) throws IOException {
6604             long partial = (size + 512) % 512;
6605             if (partial > 0) {
6606                 final int needed = 512 - (int)partial;
6607                 byte[] buffer = new byte[needed];
6608                 if (readExactly(instream, buffer, 0, needed) == needed) {
6609                     mBytes += needed;
6610                 } else throw new IOException("Unexpected EOF in padding");
6611             }
6612         }
6613 
6614         // Read a widget metadata file, returning the restored blob
readMetadata(FileMetadata info, InputStream instream)6615         void readMetadata(FileMetadata info, InputStream instream) throws IOException {
6616             // Fail on suspiciously large widget dump files
6617             if (info.size > 64 * 1024) {
6618                 throw new IOException("Metadata too big; corrupt? size=" + info.size);
6619             }
6620 
6621             byte[] buffer = new byte[(int) info.size];
6622             if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6623                 mBytes += info.size;
6624             } else throw new IOException("Unexpected EOF in widget data");
6625 
6626             String[] str = new String[1];
6627             int offset = extractLine(buffer, 0, str);
6628             int version = Integer.parseInt(str[0]);
6629             if (version == BACKUP_MANIFEST_VERSION) {
6630                 offset = extractLine(buffer, offset, str);
6631                 final String pkg = str[0];
6632                 if (info.packageName.equals(pkg)) {
6633                     // Data checks out -- the rest of the buffer is a concatenation of
6634                     // binary blobs as described in the comment at writeAppWidgetData()
6635                     ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
6636                             offset, buffer.length - offset);
6637                     DataInputStream in = new DataInputStream(bin);
6638                     while (bin.available() > 0) {
6639                         int token = in.readInt();
6640                         int size = in.readInt();
6641                         if (size > 64 * 1024) {
6642                             throw new IOException("Datum "
6643                                     + Integer.toHexString(token)
6644                                     + " too big; corrupt? size=" + info.size);
6645                         }
6646                         switch (token) {
6647                             case BACKUP_WIDGET_METADATA_TOKEN:
6648                             {
6649                                 if (MORE_DEBUG) {
6650                                     Slog.i(TAG, "Got widget metadata for " + info.packageName);
6651                                 }
6652                                 mWidgetData = new byte[size];
6653                                 in.read(mWidgetData);
6654                                 break;
6655                             }
6656                             default:
6657                             {
6658                                 if (DEBUG) {
6659                                     Slog.i(TAG, "Ignoring metadata blob "
6660                                             + Integer.toHexString(token)
6661                                             + " for " + info.packageName);
6662                                 }
6663                                 in.skipBytes(size);
6664                                 break;
6665                             }
6666                         }
6667                     }
6668                 } else {
6669                     Slog.w(TAG, "Metadata mismatch: package " + info.packageName
6670                             + " but widget data for " + pkg);
6671                 }
6672             } else {
6673                 Slog.w(TAG, "Unsupported metadata version " + version);
6674             }
6675         }
6676 
6677         // Returns a policy constant; takes a buffer arg to reduce memory churn
readAppManifest(FileMetadata info, InputStream instream)6678         RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
6679                 throws IOException {
6680             // Fail on suspiciously large manifest files
6681             if (info.size > 64 * 1024) {
6682                 throw new IOException("Restore manifest too big; corrupt? size=" + info.size);
6683             }
6684 
6685             byte[] buffer = new byte[(int) info.size];
6686             if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
6687                 mBytes += info.size;
6688             } else throw new IOException("Unexpected EOF in manifest");
6689 
6690             RestorePolicy policy = RestorePolicy.IGNORE;
6691             String[] str = new String[1];
6692             int offset = 0;
6693 
6694             try {
6695                 offset = extractLine(buffer, offset, str);
6696                 int version = Integer.parseInt(str[0]);
6697                 if (version == BACKUP_MANIFEST_VERSION) {
6698                     offset = extractLine(buffer, offset, str);
6699                     String manifestPackage = str[0];
6700                     // TODO: handle <original-package>
6701                     if (manifestPackage.equals(info.packageName)) {
6702                         offset = extractLine(buffer, offset, str);
6703                         version = Integer.parseInt(str[0]);  // app version
6704                         offset = extractLine(buffer, offset, str);
6705                         // This is the platform version, which we don't use, but we parse it
6706                         // as a safety against corruption in the manifest.
6707                         Integer.parseInt(str[0]);
6708                         offset = extractLine(buffer, offset, str);
6709                         info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
6710                         offset = extractLine(buffer, offset, str);
6711                         boolean hasApk = str[0].equals("1");
6712                         offset = extractLine(buffer, offset, str);
6713                         int numSigs = Integer.parseInt(str[0]);
6714                         if (numSigs > 0) {
6715                             Signature[] sigs = new Signature[numSigs];
6716                             for (int i = 0; i < numSigs; i++) {
6717                                 offset = extractLine(buffer, offset, str);
6718                                 sigs[i] = new Signature(str[0]);
6719                             }
6720                             mManifestSignatures.put(info.packageName, sigs);
6721 
6722                             // Okay, got the manifest info we need...
6723                             try {
6724                                 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
6725                                         info.packageName, PackageManager.GET_SIGNATURES);
6726                                 // Fall through to IGNORE if the app explicitly disallows backup
6727                                 final int flags = pkgInfo.applicationInfo.flags;
6728                                 if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
6729                                     // Restore system-uid-space packages only if they have
6730                                     // defined a custom backup agent
6731                                     if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
6732                                             || (pkgInfo.applicationInfo.backupAgentName != null)) {
6733                                         // Verify signatures against any installed version; if they
6734                                         // don't match, then we fall though and ignore the data.  The
6735                                         // signatureMatch() method explicitly ignores the signature
6736                                         // check for packages installed on the system partition, because
6737                                         // such packages are signed with the platform cert instead of
6738                                         // the app developer's cert, so they're different on every
6739                                         // device.
6740                                         if (signaturesMatch(sigs, pkgInfo)) {
6741                                             if (pkgInfo.versionCode >= version) {
6742                                                 Slog.i(TAG, "Sig + version match; taking data");
6743                                                 policy = RestorePolicy.ACCEPT;
6744                                             } else {
6745                                                 // The data is from a newer version of the app than
6746                                                 // is presently installed.  That means we can only
6747                                                 // use it if the matching apk is also supplied.
6748                                                 Slog.d(TAG, "Data version " + version
6749                                                         + " is newer than installed version "
6750                                                         + pkgInfo.versionCode + " - requiring apk");
6751                                                 policy = RestorePolicy.ACCEPT_IF_APK;
6752                                             }
6753                                         } else {
6754                                             Slog.w(TAG, "Restore manifest signatures do not match "
6755                                                     + "installed application for " + info.packageName);
6756                                         }
6757                                     } else {
6758                                         Slog.w(TAG, "Package " + info.packageName
6759                                                 + " is system level with no agent");
6760                                     }
6761                                 } else {
6762                                     if (DEBUG) Slog.i(TAG, "Restore manifest from "
6763                                             + info.packageName + " but allowBackup=false");
6764                                 }
6765                             } catch (NameNotFoundException e) {
6766                                 // Okay, the target app isn't installed.  We can process
6767                                 // the restore properly only if the dataset provides the
6768                                 // apk file and we can successfully install it.
6769                                 if (DEBUG) Slog.i(TAG, "Package " + info.packageName
6770                                         + " not installed; requiring apk in dataset");
6771                                 policy = RestorePolicy.ACCEPT_IF_APK;
6772                             }
6773 
6774                             if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
6775                                 Slog.i(TAG, "Cannot restore package " + info.packageName
6776                                         + " without the matching .apk");
6777                             }
6778                         } else {
6779                             Slog.i(TAG, "Missing signature on backed-up package "
6780                                     + info.packageName);
6781                         }
6782                     } else {
6783                         Slog.i(TAG, "Expected package " + info.packageName
6784                                 + " but restore manifest claims " + manifestPackage);
6785                     }
6786                 } else {
6787                     Slog.i(TAG, "Unknown restore manifest version " + version
6788                             + " for package " + info.packageName);
6789                 }
6790             } catch (NumberFormatException e) {
6791                 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
6792             } catch (IllegalArgumentException e) {
6793                 Slog.w(TAG, e.getMessage());
6794             }
6795 
6796             return policy;
6797         }
6798 
6799         // Builds a line from a byte buffer starting at 'offset', and returns
6800         // the index of the next unconsumed data in the buffer.
extractLine(byte[] buffer, int offset, String[] outStr)6801         int extractLine(byte[] buffer, int offset, String[] outStr) throws IOException {
6802             final int end = buffer.length;
6803             if (offset >= end) throw new IOException("Incomplete data");
6804 
6805             int pos;
6806             for (pos = offset; pos < end; pos++) {
6807                 byte c = buffer[pos];
6808                 // at LF we declare end of line, and return the next char as the
6809                 // starting point for the next time through
6810                 if (c == '\n') {
6811                     break;
6812                 }
6813             }
6814             outStr[0] = new String(buffer, offset, pos - offset);
6815             pos++;  // may be pointing an extra byte past the end but that's okay
6816             return pos;
6817         }
6818 
dumpFileMetadata(FileMetadata info)6819         void dumpFileMetadata(FileMetadata info) {
6820             if (DEBUG) {
6821                 StringBuilder b = new StringBuilder(128);
6822 
6823                 // mode string
6824                 b.append((info.type == BackupAgent.TYPE_DIRECTORY) ? 'd' : '-');
6825                 b.append(((info.mode & 0400) != 0) ? 'r' : '-');
6826                 b.append(((info.mode & 0200) != 0) ? 'w' : '-');
6827                 b.append(((info.mode & 0100) != 0) ? 'x' : '-');
6828                 b.append(((info.mode & 0040) != 0) ? 'r' : '-');
6829                 b.append(((info.mode & 0020) != 0) ? 'w' : '-');
6830                 b.append(((info.mode & 0010) != 0) ? 'x' : '-');
6831                 b.append(((info.mode & 0004) != 0) ? 'r' : '-');
6832                 b.append(((info.mode & 0002) != 0) ? 'w' : '-');
6833                 b.append(((info.mode & 0001) != 0) ? 'x' : '-');
6834                 b.append(String.format(" %9d ", info.size));
6835 
6836                 Date stamp = new Date(info.mtime);
6837                 b.append(new SimpleDateFormat("MMM dd HH:mm:ss ").format(stamp));
6838 
6839                 b.append(info.packageName);
6840                 b.append(" :: ");
6841                 b.append(info.domain);
6842                 b.append(" :: ");
6843                 b.append(info.path);
6844 
6845                 Slog.i(TAG, b.toString());
6846             }
6847         }
6848         // Consume a tar file header block [sequence] and accumulate the relevant metadata
readTarHeaders(InputStream instream)6849         FileMetadata readTarHeaders(InputStream instream) throws IOException {
6850             byte[] block = new byte[512];
6851             FileMetadata info = null;
6852 
6853             boolean gotHeader = readTarHeader(instream, block);
6854             if (gotHeader) {
6855                 try {
6856                     // okay, presume we're okay, and extract the various metadata
6857                     info = new FileMetadata();
6858                     info.size = extractRadix(block, 124, 12, 8);
6859                     info.mtime = extractRadix(block, 136, 12, 8);
6860                     info.mode = extractRadix(block, 100, 8, 8);
6861 
6862                     info.path = extractString(block, 345, 155); // prefix
6863                     String path = extractString(block, 0, 100);
6864                     if (path.length() > 0) {
6865                         if (info.path.length() > 0) info.path += '/';
6866                         info.path += path;
6867                     }
6868 
6869                     // tar link indicator field: 1 byte at offset 156 in the header.
6870                     int typeChar = block[156];
6871                     if (typeChar == 'x') {
6872                         // pax extended header, so we need to read that
6873                         gotHeader = readPaxExtendedHeader(instream, info);
6874                         if (gotHeader) {
6875                             // and after a pax extended header comes another real header -- read
6876                             // that to find the real file type
6877                             gotHeader = readTarHeader(instream, block);
6878                         }
6879                         if (!gotHeader) throw new IOException("Bad or missing pax header");
6880 
6881                         typeChar = block[156];
6882                     }
6883 
6884                     switch (typeChar) {
6885                         case '0': info.type = BackupAgent.TYPE_FILE; break;
6886                         case '5': {
6887                             info.type = BackupAgent.TYPE_DIRECTORY;
6888                             if (info.size != 0) {
6889                                 Slog.w(TAG, "Directory entry with nonzero size in header");
6890                                 info.size = 0;
6891                             }
6892                             break;
6893                         }
6894                         case 0: {
6895                             // presume EOF
6896                             if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
6897                             return null;
6898                         }
6899                         default: {
6900                             Slog.e(TAG, "Unknown tar entity type: " + typeChar);
6901                             throw new IOException("Unknown entity type " + typeChar);
6902                         }
6903                     }
6904 
6905                     // Parse out the path
6906                     //
6907                     // first: apps/shared/unrecognized
6908                     if (FullBackup.SHARED_PREFIX.regionMatches(0,
6909                             info.path, 0, FullBackup.SHARED_PREFIX.length())) {
6910                         // File in shared storage.  !!! TODO: implement this.
6911                         info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
6912                         info.packageName = SHARED_BACKUP_AGENT_PACKAGE;
6913                         info.domain = FullBackup.SHARED_STORAGE_TOKEN;
6914                         if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
6915                     } else if (FullBackup.APPS_PREFIX.regionMatches(0,
6916                             info.path, 0, FullBackup.APPS_PREFIX.length())) {
6917                         // App content!  Parse out the package name and domain
6918 
6919                         // strip the apps/ prefix
6920                         info.path = info.path.substring(FullBackup.APPS_PREFIX.length());
6921 
6922                         // extract the package name
6923                         int slash = info.path.indexOf('/');
6924                         if (slash < 0) throw new IOException("Illegal semantic path in " + info.path);
6925                         info.packageName = info.path.substring(0, slash);
6926                         info.path = info.path.substring(slash+1);
6927 
6928                         // if it's a manifest or metadata payload we're done, otherwise parse
6929                         // out the domain into which the file will be restored
6930                         if (!info.path.equals(BACKUP_MANIFEST_FILENAME)
6931                                 && !info.path.equals(BACKUP_METADATA_FILENAME)) {
6932                             slash = info.path.indexOf('/');
6933                             if (slash < 0) throw new IOException("Illegal semantic path in non-manifest " + info.path);
6934                             info.domain = info.path.substring(0, slash);
6935                             info.path = info.path.substring(slash + 1);
6936                         }
6937                     }
6938                 } catch (IOException e) {
6939                     if (DEBUG) {
6940                         Slog.e(TAG, "Parse error in header: " + e.getMessage());
6941                         HEXLOG(block);
6942                     }
6943                     throw e;
6944                 }
6945             }
6946             return info;
6947         }
6948 
HEXLOG(byte[] block)6949         private void HEXLOG(byte[] block) {
6950             int offset = 0;
6951             int todo = block.length;
6952             StringBuilder buf = new StringBuilder(64);
6953             while (todo > 0) {
6954                 buf.append(String.format("%04x   ", offset));
6955                 int numThisLine = (todo > 16) ? 16 : todo;
6956                 for (int i = 0; i < numThisLine; i++) {
6957                     buf.append(String.format("%02x ", block[offset+i]));
6958                 }
6959                 Slog.i("hexdump", buf.toString());
6960                 buf.setLength(0);
6961                 todo -= numThisLine;
6962                 offset += numThisLine;
6963             }
6964         }
6965 
6966         // Read exactly the given number of bytes into a buffer at the stated offset.
6967         // Returns false if EOF is encountered before the requested number of bytes
6968         // could be read.
readExactly(InputStream in, byte[] buffer, int offset, int size)6969         int readExactly(InputStream in, byte[] buffer, int offset, int size)
6970                 throws IOException {
6971             if (size <= 0) throw new IllegalArgumentException("size must be > 0");
6972 
6973             int soFar = 0;
6974             while (soFar < size) {
6975                 int nRead = in.read(buffer, offset + soFar, size - soFar);
6976                 if (nRead <= 0) {
6977                     if (MORE_DEBUG) Slog.w(TAG, "- wanted exactly " + size + " but got only " + soFar);
6978                     break;
6979                 }
6980                 soFar += nRead;
6981             }
6982             return soFar;
6983         }
6984 
readTarHeader(InputStream instream, byte[] block)6985         boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
6986             final int got = readExactly(instream, block, 0, 512);
6987             if (got == 0) return false;     // Clean EOF
6988             if (got < 512) throw new IOException("Unable to read full block header");
6989             mBytes += 512;
6990             return true;
6991         }
6992 
6993         // overwrites 'info' fields based on the pax extended header
readPaxExtendedHeader(InputStream instream, FileMetadata info)6994         boolean readPaxExtendedHeader(InputStream instream, FileMetadata info)
6995                 throws IOException {
6996             // We should never see a pax extended header larger than this
6997             if (info.size > 32*1024) {
6998                 Slog.w(TAG, "Suspiciously large pax header size " + info.size
6999                         + " - aborting");
7000                 throw new IOException("Sanity failure: pax header size " + info.size);
7001             }
7002 
7003             // read whole blocks, not just the content size
7004             int numBlocks = (int)((info.size + 511) >> 9);
7005             byte[] data = new byte[numBlocks * 512];
7006             if (readExactly(instream, data, 0, data.length) < data.length) {
7007                 throw new IOException("Unable to read full pax header");
7008             }
7009             mBytes += data.length;
7010 
7011             final int contentSize = (int) info.size;
7012             int offset = 0;
7013             do {
7014                 // extract the line at 'offset'
7015                 int eol = offset+1;
7016                 while (eol < contentSize && data[eol] != ' ') eol++;
7017                 if (eol >= contentSize) {
7018                     // error: we just hit EOD looking for the end of the size field
7019                     throw new IOException("Invalid pax data");
7020                 }
7021                 // eol points to the space between the count and the key
7022                 int linelen = (int) extractRadix(data, offset, eol - offset, 10);
7023                 int key = eol + 1;  // start of key=value
7024                 eol = offset + linelen - 1; // trailing LF
7025                 int value;
7026                 for (value = key+1; data[value] != '=' && value <= eol; value++);
7027                 if (value > eol) {
7028                     throw new IOException("Invalid pax declaration");
7029                 }
7030 
7031                 // pax requires that key/value strings be in UTF-8
7032                 String keyStr = new String(data, key, value-key, "UTF-8");
7033                 // -1 to strip the trailing LF
7034                 String valStr = new String(data, value+1, eol-value-1, "UTF-8");
7035 
7036                 if ("path".equals(keyStr)) {
7037                     info.path = valStr;
7038                 } else if ("size".equals(keyStr)) {
7039                     info.size = Long.parseLong(valStr);
7040                 } else {
7041                     if (DEBUG) Slog.i(TAG, "Unhandled pax key: " + key);
7042                 }
7043 
7044                 offset += linelen;
7045             } while (offset < contentSize);
7046 
7047             return true;
7048         }
7049 
extractRadix(byte[] data, int offset, int maxChars, int radix)7050         long extractRadix(byte[] data, int offset, int maxChars, int radix)
7051                 throws IOException {
7052             long value = 0;
7053             final int end = offset + maxChars;
7054             for (int i = offset; i < end; i++) {
7055                 final byte b = data[i];
7056                 // Numeric fields in tar can terminate with either NUL or SPC
7057                 if (b == 0 || b == ' ') break;
7058                 if (b < '0' || b > ('0' + radix - 1)) {
7059                     throw new IOException("Invalid number in header: '" + (char)b + "' for radix " + radix);
7060                 }
7061                 value = radix * value + (b - '0');
7062             }
7063             return value;
7064         }
7065 
extractString(byte[] data, int offset, int maxChars)7066         String extractString(byte[] data, int offset, int maxChars) throws IOException {
7067             final int end = offset + maxChars;
7068             int eos = offset;
7069             // tar string fields terminate early with a NUL
7070             while (eos < end && data[eos] != 0) eos++;
7071             return new String(data, offset, eos-offset, "US-ASCII");
7072         }
7073 
sendStartRestore()7074         void sendStartRestore() {
7075             if (mObserver != null) {
7076                 try {
7077                     mObserver.onStartRestore();
7078                 } catch (RemoteException e) {
7079                     Slog.w(TAG, "full restore observer went away: startRestore");
7080                     mObserver = null;
7081                 }
7082             }
7083         }
7084 
sendOnRestorePackage(String name)7085         void sendOnRestorePackage(String name) {
7086             if (mObserver != null) {
7087                 try {
7088                     // TODO: use a more user-friendly name string
7089                     mObserver.onRestorePackage(name);
7090                 } catch (RemoteException e) {
7091                     Slog.w(TAG, "full restore observer went away: restorePackage");
7092                     mObserver = null;
7093                 }
7094             }
7095         }
7096 
sendEndRestore()7097         void sendEndRestore() {
7098             if (mObserver != null) {
7099                 try {
7100                     mObserver.onEndRestore();
7101                 } catch (RemoteException e) {
7102                     Slog.w(TAG, "full restore observer went away: endRestore");
7103                     mObserver = null;
7104                 }
7105             }
7106         }
7107     }
7108 
7109     // ----- Restore handling -----
7110 
7111     // new style: we only store the SHA-1 hashes of each sig, not the full block
signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target)7112     static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
7113         if (target == null) {
7114             return false;
7115         }
7116 
7117         // If the target resides on the system partition, we allow it to restore
7118         // data from the like-named package in a restore set even if the signatures
7119         // do not match.  (Unlike general applications, those flashed to the system
7120         // partition will be signed with the device's platform certificate, so on
7121         // different phones the same system app will have different signatures.)
7122         if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
7123             if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
7124             return true;
7125         }
7126 
7127         // Allow unsigned apps, but not signed on one device and unsigned on the other
7128         // !!! TODO: is this the right policy?
7129         Signature[] deviceSigs = target.signatures;
7130         if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
7131                 + " device=" + deviceSigs);
7132         if ((storedSigHashes == null || storedSigHashes.size() == 0)
7133                 && (deviceSigs == null || deviceSigs.length == 0)) {
7134             return true;
7135         }
7136         if (storedSigHashes == null || deviceSigs == null) {
7137             return false;
7138         }
7139 
7140         // !!! TODO: this demands that every stored signature match one
7141         // that is present on device, and does not demand the converse.
7142         // Is this this right policy?
7143         final int nStored = storedSigHashes.size();
7144         final int nDevice = deviceSigs.length;
7145 
7146         // hash each on-device signature
7147         ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
7148         for (int i = 0; i < nDevice; i++) {
7149             deviceHashes.add(hashSignature(deviceSigs[i]));
7150         }
7151 
7152         // now ensure that each stored sig (hash) matches an on-device sig (hash)
7153         for (int n = 0; n < nStored; n++) {
7154             boolean match = false;
7155             final byte[] storedHash = storedSigHashes.get(n);
7156             for (int i = 0; i < nDevice; i++) {
7157                 if (Arrays.equals(storedHash, deviceHashes.get(i))) {
7158                     match = true;
7159                     break;
7160                 }
7161             }
7162             // match is false when no on-device sig matched one of the stored ones
7163             if (!match) {
7164                 return false;
7165             }
7166         }
7167 
7168         return true;
7169     }
7170 
hashSignature(Signature sig)7171     static byte[] hashSignature(Signature sig) {
7172         try {
7173             MessageDigest digest = MessageDigest.getInstance("SHA-256");
7174             digest.update(sig.toByteArray());
7175             return digest.digest();
7176         } catch (NoSuchAlgorithmException e) {
7177             Slog.w(TAG, "No SHA-256 algorithm found!");
7178         }
7179         return null;
7180     }
7181 
7182     // Old style: directly match the stored vs on device signature blocks
signaturesMatch(Signature[] storedSigs, PackageInfo target)7183     static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
7184         if (target == null) {
7185             return false;
7186         }
7187 
7188         // If the target resides on the system partition, we allow it to restore
7189         // data from the like-named package in a restore set even if the signatures
7190         // do not match.  (Unlike general applications, those flashed to the system
7191         // partition will be signed with the device's platform certificate, so on
7192         // different phones the same system app will have different signatures.)
7193         if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
7194             if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
7195             return true;
7196         }
7197 
7198         // Allow unsigned apps, but not signed on one device and unsigned on the other
7199         // !!! TODO: is this the right policy?
7200         Signature[] deviceSigs = target.signatures;
7201         if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
7202                 + " device=" + deviceSigs);
7203         if ((storedSigs == null || storedSigs.length == 0)
7204                 && (deviceSigs == null || deviceSigs.length == 0)) {
7205             return true;
7206         }
7207         if (storedSigs == null || deviceSigs == null) {
7208             return false;
7209         }
7210 
7211         // !!! TODO: this demands that every stored signature match one
7212         // that is present on device, and does not demand the converse.
7213         // Is this this right policy?
7214         int nStored = storedSigs.length;
7215         int nDevice = deviceSigs.length;
7216 
7217         for (int i=0; i < nStored; i++) {
7218             boolean match = false;
7219             for (int j=0; j < nDevice; j++) {
7220                 if (storedSigs[i].equals(deviceSigs[j])) {
7221                     match = true;
7222                     break;
7223                 }
7224             }
7225             if (!match) {
7226                 return false;
7227             }
7228         }
7229         return true;
7230     }
7231 
7232     // Used by both incremental and full restore
restoreWidgetData(String packageName, byte[] widgetData)7233     void restoreWidgetData(String packageName, byte[] widgetData) {
7234         // Apply the restored widget state and generate the ID update for the app
7235         AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
7236     }
7237 
7238     // *****************************
7239     // NEW UNIFIED RESTORE IMPLEMENTATION
7240     // *****************************
7241 
7242     // states of the unified-restore state machine
7243     enum UnifiedRestoreState {
7244         INITIAL,
7245         RUNNING_QUEUE,
7246         RESTORE_KEYVALUE,
7247         RESTORE_FULL,
7248         RESTORE_FINISHED,
7249         FINAL
7250     }
7251 
7252     class PerformUnifiedRestoreTask implements BackupRestoreTask {
7253         // Transport we're working with to do the restore
7254         private IBackupTransport mTransport;
7255 
7256         // Where per-transport saved state goes
7257         File mStateDir;
7258 
7259         // Restore observer; may be null
7260         private IRestoreObserver mObserver;
7261 
7262         // Token identifying the dataset to the transport
7263         private long mToken;
7264 
7265         // When this is a restore-during-install, this is the token identifying the
7266         // operation to the Package Manager, and we must ensure that we let it know
7267         // when we're finished.
7268         private int mPmToken;
7269 
7270         // Is this a whole-system restore, i.e. are we establishing a new ancestral
7271         // dataset to base future restore-at-install operations from?
7272         private boolean mIsSystemRestore;
7273 
7274         // If this is a single-package restore, what package are we interested in?
7275         private PackageInfo mTargetPackage;
7276 
7277         // In all cases, the calculated list of packages that we are trying to restore
7278         private List<PackageInfo> mAcceptSet;
7279 
7280         // Our bookkeeping about the ancestral dataset
7281         private PackageManagerBackupAgent mPmAgent;
7282 
7283         // Currently-bound backup agent for restore + restoreFinished purposes
7284         private IBackupAgent mAgent;
7285 
7286         // What sort of restore we're doing now
7287         private RestoreDescription mRestoreDescription;
7288 
7289         // The package we're currently restoring
7290         private PackageInfo mCurrentPackage;
7291 
7292         // Widget-related data handled as part of this restore operation
7293         private byte[] mWidgetData;
7294 
7295         // Number of apps restored in this pass
7296         private int mCount;
7297 
7298         // When did we start?
7299         private long mStartRealtime;
7300 
7301         // State machine progress
7302         private UnifiedRestoreState mState;
7303 
7304         // How are things going?
7305         private int mStatus;
7306 
7307         // Done?
7308         private boolean mFinished;
7309 
7310         // Key/value: bookkeeping about staged data and files for agent access
7311         private File mBackupDataName;
7312         private File mStageName;
7313         private File mSavedStateName;
7314         private File mNewStateName;
7315         ParcelFileDescriptor mBackupData;
7316         ParcelFileDescriptor mNewState;
7317 
7318         // Invariant: mWakelock is already held, and this task is responsible for
7319         // releasing it at the end of the restore operation.
PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer, long restoreSetToken, PackageInfo targetPackage, int pmToken, boolean isFullSystemRestore, String[] filterSet)7320         PerformUnifiedRestoreTask(IBackupTransport transport, IRestoreObserver observer,
7321                 long restoreSetToken, PackageInfo targetPackage, int pmToken,
7322                 boolean isFullSystemRestore, String[] filterSet) {
7323             mState = UnifiedRestoreState.INITIAL;
7324             mStartRealtime = SystemClock.elapsedRealtime();
7325 
7326             mTransport = transport;
7327             mObserver = observer;
7328             mToken = restoreSetToken;
7329             mPmToken = pmToken;
7330             mTargetPackage = targetPackage;
7331             mIsSystemRestore = isFullSystemRestore;
7332             mFinished = false;
7333 
7334             if (targetPackage != null) {
7335                 // Single package restore
7336                 mAcceptSet = new ArrayList<PackageInfo>();
7337                 mAcceptSet.add(targetPackage);
7338             } else {
7339                 // Everything possible, or a target set
7340                 if (filterSet == null) {
7341                     // We want everything and a pony
7342                     List<PackageInfo> apps =
7343                             PackageManagerBackupAgent.getStorableApplications(mPackageManager);
7344                     filterSet = packagesToNames(apps);
7345                     if (DEBUG) {
7346                         Slog.i(TAG, "Full restore; asking about " + filterSet.length + " apps");
7347                     }
7348                 }
7349 
7350                 mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
7351 
7352                 // Pro tem, we insist on moving the settings provider package to last place.
7353                 // Keep track of whether it's in the list, and bump it down if so.  We also
7354                 // want to do the system package itself first if it's called for.
7355                 boolean hasSystem = false;
7356                 boolean hasSettings = false;
7357                 for (int i = 0; i < filterSet.length; i++) {
7358                     try {
7359                         PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
7360                         if ("android".equals(info.packageName)) {
7361                             hasSystem = true;
7362                             continue;
7363                         }
7364                         if (SETTINGS_PACKAGE.equals(info.packageName)) {
7365                             hasSettings = true;
7366                             continue;
7367                         }
7368 
7369                         if (appIsEligibleForBackup(info.applicationInfo)) {
7370                             mAcceptSet.add(info);
7371                         }
7372                     } catch (NameNotFoundException e) {
7373                         // requested package name doesn't exist; ignore it
7374                     }
7375                 }
7376                 if (hasSystem) {
7377                     try {
7378                         mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
7379                     } catch (NameNotFoundException e) {
7380                         // won't happen; we know a priori that it's valid
7381                     }
7382                 }
7383                 if (hasSettings) {
7384                     try {
7385                         mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
7386                     } catch (NameNotFoundException e) {
7387                         // this one is always valid too
7388                     }
7389                 }
7390             }
7391 
7392             if (MORE_DEBUG) {
7393                 Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
7394                 for (PackageInfo info : mAcceptSet) {
7395                     Slog.v(TAG, "   " + info.packageName);
7396                 }
7397             }
7398         }
7399 
packagesToNames(List<PackageInfo> apps)7400         private String[] packagesToNames(List<PackageInfo> apps) {
7401             final int N = apps.size();
7402             String[] names = new String[N];
7403             for (int i = 0; i < N; i++) {
7404                 names[i] = apps.get(i).packageName;
7405             }
7406             return names;
7407         }
7408 
7409         // Execute one tick of whatever state machine the task implements
7410         @Override
execute()7411         public void execute() {
7412             if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step " + mState);
7413             switch (mState) {
7414                 case INITIAL:
7415                     startRestore();
7416                     break;
7417 
7418                 case RUNNING_QUEUE:
7419                     dispatchNextRestore();
7420                     break;
7421 
7422                 case RESTORE_KEYVALUE:
7423                     restoreKeyValue();
7424                     break;
7425 
7426                 case RESTORE_FULL:
7427                     restoreFull();
7428                     break;
7429 
7430                 case RESTORE_FINISHED:
7431                     restoreFinished();
7432                     break;
7433 
7434                 case FINAL:
7435                     if (!mFinished) finalizeRestore();
7436                     else {
7437                         Slog.e(TAG, "Duplicate finish");
7438                     }
7439                     mFinished = true;
7440                     break;
7441             }
7442         }
7443 
7444         /*
7445          * SKETCH OF OPERATION
7446          *
7447          * create one of these PerformUnifiedRestoreTask objects, telling it which
7448          * dataset & transport to address, and then parameters within the restore
7449          * operation: single target package vs many, etc.
7450          *
7451          * 1. transport.startRestore(token, list-of-packages).  If we need @pm@  it is
7452          * always placed first and the settings provider always placed last [for now].
7453          *
7454          * 1a [if we needed @pm@ then nextRestorePackage() and restore the PMBA inline]
7455          *
7456          *   [ state change => RUNNING_QUEUE ]
7457          *
7458          * NOW ITERATE:
7459          *
7460          * { 3. t.nextRestorePackage()
7461          *   4. does the metadata for this package allow us to restore it?
7462          *      does the on-disk app permit us to restore it? [re-check allowBackup etc]
7463          *   5. is this a key/value dataset?  => key/value agent restore
7464          *       [ state change => RESTORE_KEYVALUE ]
7465          *       5a. spin up agent
7466          *       5b. t.getRestoreData() to stage it properly
7467          *       5c. call into agent to perform restore
7468          *       5d. tear down agent
7469          *       [ state change => RUNNING_QUEUE ]
7470          *
7471          *   6. else it's a stream dataset:
7472          *       [ state change => RESTORE_FULL ]
7473          *       6a. instantiate the engine for a stream restore: engine handles agent lifecycles
7474          *       6b. spin off engine runner on separate thread
7475          *       6c. ITERATE getNextFullRestoreDataChunk() and copy data to engine runner socket
7476          *       [ state change => RUNNING_QUEUE ]
7477          * }
7478          *
7479          *   [ state change => FINAL ]
7480          *
7481          * 7. t.finishRestore(), release wakelock, etc.
7482          *
7483          *
7484          */
7485 
7486         // state INITIAL : set up for the restore and read the metadata if necessary
startRestore()7487         private  void startRestore() {
7488             sendStartRestore(mAcceptSet.size());
7489 
7490             // If we're starting a full-system restore, set up to begin widget ID remapping
7491             if (mIsSystemRestore) {
7492                 AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER);
7493             }
7494 
7495             try {
7496                 String transportDir = mTransport.transportDirName();
7497                 mStateDir = new File(mBaseStateDir, transportDir);
7498 
7499                 // Fetch the current metadata from the dataset first
7500                 PackageInfo pmPackage = new PackageInfo();
7501                 pmPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7502                 mAcceptSet.add(0, pmPackage);
7503 
7504                 PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
7505                 mStatus = mTransport.startRestore(mToken, packages);
7506                 if (mStatus != BackupTransport.TRANSPORT_OK) {
7507                     Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
7508                     mStatus = BackupTransport.TRANSPORT_ERROR;
7509                     executeNextState(UnifiedRestoreState.FINAL);
7510                     return;
7511                 }
7512 
7513                 RestoreDescription desc = mTransport.nextRestorePackage();
7514                 if (desc == null) {
7515                     Slog.e(TAG, "No restore metadata available; halting");
7516                     mStatus = BackupTransport.TRANSPORT_ERROR;
7517                     executeNextState(UnifiedRestoreState.FINAL);
7518                     return;
7519                 }
7520                 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
7521                     Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
7522                     mStatus = BackupTransport.TRANSPORT_ERROR;
7523                     executeNextState(UnifiedRestoreState.FINAL);
7524                     return;
7525                 }
7526 
7527                 // Pull the Package Manager metadata from the restore set first
7528                 mCurrentPackage = new PackageInfo();
7529                 mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
7530                 mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
7531                 mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
7532                 if (MORE_DEBUG) {
7533                     Slog.v(TAG, "initiating restore for PMBA");
7534                 }
7535                 initiateOneRestore(mCurrentPackage, 0);
7536                 // The PM agent called operationComplete() already, because our invocation
7537                 // of it is process-local and therefore synchronous.  That means that the
7538                 // next-state message (RUNNING_QUEUE) is already enqueued.  Only if we're
7539                 // unable to proceed with running the queue do we remove that pending
7540                 // message and jump straight to the FINAL state.  Because this was
7541                 // synchronous we also know that we should cancel the pending timeout
7542                 // message.
7543                 mBackupHandler.removeMessages(MSG_TIMEOUT);
7544 
7545                 // Verify that the backup set includes metadata.  If not, we can't do
7546                 // signature/version verification etc, so we simply do not proceed with
7547                 // the restore operation.
7548                 if (!mPmAgent.hasMetadata()) {
7549                     Slog.e(TAG, "No restore metadata available, so not restoring");
7550                     EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7551                             PACKAGE_MANAGER_SENTINEL,
7552                             "Package manager restore metadata missing");
7553                     mStatus = BackupTransport.TRANSPORT_ERROR;
7554                     mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7555                     executeNextState(UnifiedRestoreState.FINAL);
7556                     return;
7557                 }
7558 
7559                 // Success; cache the metadata and continue as expected with the
7560                 // next state already enqueued
7561 
7562             } catch (RemoteException e) {
7563                 // If we lost the transport at any time, halt
7564                 Slog.e(TAG, "Unable to contact transport for restore");
7565                 mStatus = BackupTransport.TRANSPORT_ERROR;
7566                 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
7567                 executeNextState(UnifiedRestoreState.FINAL);
7568                 return;
7569             }
7570         }
7571 
7572         // state RUNNING_QUEUE : figure out what the next thing to be restored is,
7573         // and fire the appropriate next step
dispatchNextRestore()7574         private void dispatchNextRestore() {
7575             UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
7576             try {
7577                 mRestoreDescription = mTransport.nextRestorePackage();
7578                 final String pkgName = (mRestoreDescription != null)
7579                         ? mRestoreDescription.getPackageName() : null;
7580                 if (pkgName == null) {
7581                     Slog.e(TAG, "Failure getting next package name");
7582                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7583                     nextState = UnifiedRestoreState.FINAL;
7584                     return;
7585                 } else if (mRestoreDescription == RestoreDescription.NO_MORE_PACKAGES) {
7586                     // Yay we've reached the end cleanly
7587                     if (DEBUG) {
7588                         Slog.v(TAG, "No more packages; finishing restore");
7589                     }
7590                     int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
7591                     EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
7592                     nextState = UnifiedRestoreState.FINAL;
7593                     return;
7594                 }
7595 
7596                 if (DEBUG) {
7597                     Slog.i(TAG, "Next restore package: " + mRestoreDescription);
7598                 }
7599                 sendOnRestorePackage(pkgName);
7600 
7601                 Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
7602                 if (metaInfo == null) {
7603                     Slog.e(TAG, "No metadata for " + pkgName);
7604                     EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
7605                             "Package metadata missing");
7606                     nextState = UnifiedRestoreState.RUNNING_QUEUE;
7607                     return;
7608                 }
7609 
7610                 try {
7611                     mCurrentPackage = mPackageManager.getPackageInfo(
7612                             pkgName, PackageManager.GET_SIGNATURES);
7613                 } catch (NameNotFoundException e) {
7614                     // Whoops, we thought we could restore this package but it
7615                     // turns out not to be present.  Skip it.
7616                     Slog.e(TAG, "Package not present: " + pkgName);
7617                     EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
7618                             "Package missing on device");
7619                     nextState = UnifiedRestoreState.RUNNING_QUEUE;
7620                     return;
7621                 }
7622 
7623                 if (metaInfo.versionCode > mCurrentPackage.versionCode) {
7624                     // Data is from a "newer" version of the app than we have currently
7625                     // installed.  If the app has not declared that it is prepared to
7626                     // handle this case, we do not attempt the restore.
7627                     if ((mCurrentPackage.applicationInfo.flags
7628                             & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
7629                         String message = "Version " + metaInfo.versionCode
7630                                 + " > installed version " + mCurrentPackage.versionCode;
7631                         Slog.w(TAG, "Package " + pkgName + ": " + message);
7632                         EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7633                                 pkgName, message);
7634                         nextState = UnifiedRestoreState.RUNNING_QUEUE;
7635                         return;
7636                     } else {
7637                         if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
7638                                 + " > installed " + mCurrentPackage.versionCode
7639                                 + " but restoreAnyVersion");
7640                     }
7641                 }
7642 
7643                 if (MORE_DEBUG) Slog.v(TAG, "Package " + pkgName
7644                         + " restore version [" + metaInfo.versionCode
7645                         + "] is compatible with installed version ["
7646                         + mCurrentPackage.versionCode + "]");
7647 
7648                 // Reset per-package preconditions and fire the appropriate next state
7649                 mWidgetData = null;
7650                 final int type = mRestoreDescription.getDataType();
7651                 if (type == RestoreDescription.TYPE_KEY_VALUE) {
7652                     nextState = UnifiedRestoreState.RESTORE_KEYVALUE;
7653                 } else if (type == RestoreDescription.TYPE_FULL_STREAM) {
7654                     nextState = UnifiedRestoreState.RESTORE_FULL;
7655                 } else {
7656                     // Unknown restore type; ignore this package and move on
7657                     Slog.e(TAG, "Unrecognized restore type " + type);
7658                     nextState = UnifiedRestoreState.RUNNING_QUEUE;
7659                     return;
7660                 }
7661             } catch (RemoteException e) {
7662                 Slog.e(TAG, "Can't get next target from transport; ending restore");
7663                 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7664                 nextState = UnifiedRestoreState.FINAL;
7665                 return;
7666             } finally {
7667                 executeNextState(nextState);
7668             }
7669         }
7670 
7671         // state RESTORE_KEYVALUE : restore one package via key/value API set
restoreKeyValue()7672         private void restoreKeyValue() {
7673             // Initiating the restore will pass responsibility for the state machine's
7674             // progress to the agent callback, so we do not always execute the
7675             // next state here.
7676             final String packageName = mCurrentPackage.packageName;
7677             // Validate some semantic requirements that apply in this way
7678             // only to the key/value restore API flow
7679             if (mCurrentPackage.applicationInfo.backupAgentName == null
7680                     || "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
7681                 if (MORE_DEBUG) {
7682                     Slog.i(TAG, "Data exists for package " + packageName
7683                             + " but app has no agent; skipping");
7684                 }
7685                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7686                         "Package has no agent");
7687                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7688                 return;
7689             }
7690 
7691             Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
7692             if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
7693                 Slog.w(TAG, "Signature mismatch restoring " + packageName);
7694                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7695                         "Signature mismatch");
7696                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7697                 return;
7698             }
7699 
7700             // Good to go!  Set up and bind the agent...
7701             mAgent = bindToAgentSynchronous(
7702                     mCurrentPackage.applicationInfo,
7703                     IApplicationThread.BACKUP_MODE_INCREMENTAL);
7704             if (mAgent == null) {
7705                 Slog.w(TAG, "Can't find backup agent for " + packageName);
7706                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
7707                         "Restore agent missing");
7708                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7709                 return;
7710             }
7711 
7712             // And then finally start the restore on this agent
7713             try {
7714                 initiateOneRestore(mCurrentPackage, metaInfo.versionCode);
7715                 ++mCount;
7716             } catch (Exception e) {
7717                 Slog.e(TAG, "Error when attempting restore: " + e.toString());
7718                 keyValueAgentErrorCleanup();
7719                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7720             }
7721         }
7722 
7723         // Guts of a key/value restore operation
initiateOneRestore(PackageInfo app, int appVersionCode)7724         void initiateOneRestore(PackageInfo app, int appVersionCode) {
7725             final String packageName = app.packageName;
7726 
7727             if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
7728 
7729             // !!! TODO: get the dirs from the transport
7730             mBackupDataName = new File(mDataDir, packageName + ".restore");
7731             mStageName = new File(mDataDir, packageName + ".stage");
7732             mNewStateName = new File(mStateDir, packageName + ".new");
7733             mSavedStateName = new File(mStateDir, packageName);
7734 
7735             // don't stage the 'android' package where the wallpaper data lives.  this is
7736             // an optimization: we know there's no widget data hosted/published by that
7737             // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
7738             // data following the download.
7739             boolean staging = !packageName.equals("android");
7740             ParcelFileDescriptor stage;
7741             File downloadFile = (staging) ? mStageName : mBackupDataName;
7742 
7743             final int token = generateToken();
7744             try {
7745                 // Run the transport's restore pass
7746                 stage = ParcelFileDescriptor.open(downloadFile,
7747                         ParcelFileDescriptor.MODE_READ_WRITE |
7748                         ParcelFileDescriptor.MODE_CREATE |
7749                         ParcelFileDescriptor.MODE_TRUNCATE);
7750 
7751                 if (!SELinux.restorecon(mBackupDataName)) {
7752                     if (MORE_DEBUG) Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
7753                 }
7754 
7755                 if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
7756                     // Transport-level failure, so we wind everything up and
7757                     // terminate the restore operation.
7758                     Slog.e(TAG, "Error getting restore data for " + packageName);
7759                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7760                     stage.close();
7761                     downloadFile.delete();
7762                     executeNextState(UnifiedRestoreState.FINAL);
7763                     return;
7764                 }
7765 
7766                 // We have the data from the transport. Now we extract and strip
7767                 // any per-package metadata (typically widget-related information)
7768                 // if appropriate
7769                 if (staging) {
7770                     stage.close();
7771                     stage = ParcelFileDescriptor.open(downloadFile,
7772                             ParcelFileDescriptor.MODE_READ_ONLY);
7773 
7774                     mBackupData = ParcelFileDescriptor.open(mBackupDataName,
7775                             ParcelFileDescriptor.MODE_READ_WRITE |
7776                             ParcelFileDescriptor.MODE_CREATE |
7777                             ParcelFileDescriptor.MODE_TRUNCATE);
7778 
7779                     BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
7780                     BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
7781                     byte[] buffer = new byte[8192]; // will grow when needed
7782                     while (in.readNextHeader()) {
7783                         final String key = in.getKey();
7784                         final int size = in.getDataSize();
7785 
7786                         // is this a special key?
7787                         if (key.equals(KEY_WIDGET_STATE)) {
7788                             if (DEBUG) {
7789                                 Slog.i(TAG, "Restoring widget state for " + packageName);
7790                             }
7791                             mWidgetData = new byte[size];
7792                             in.readEntityData(mWidgetData, 0, size);
7793                         } else {
7794                             if (size > buffer.length) {
7795                                 buffer = new byte[size];
7796                             }
7797                             in.readEntityData(buffer, 0, size);
7798                             out.writeEntityHeader(key, size);
7799                             out.writeEntityData(buffer, size);
7800                         }
7801                     }
7802 
7803                     mBackupData.close();
7804                 }
7805 
7806                 // Okay, we have the data.  Now have the agent do the restore.
7807                 stage.close();
7808                 mBackupData = ParcelFileDescriptor.open(mBackupDataName,
7809                         ParcelFileDescriptor.MODE_READ_ONLY);
7810 
7811                 mNewState = ParcelFileDescriptor.open(mNewStateName,
7812                         ParcelFileDescriptor.MODE_READ_WRITE |
7813                         ParcelFileDescriptor.MODE_CREATE |
7814                         ParcelFileDescriptor.MODE_TRUNCATE);
7815 
7816                 // Kick off the restore, checking for hung agents.  The timeout or
7817                 // the operationComplete() callback will schedule the next step,
7818                 // so we do not do that here.
7819                 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
7820                 mAgent.doRestore(mBackupData, appVersionCode, mNewState,
7821                         token, mBackupManagerBinder);
7822             } catch (Exception e) {
7823                 Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
7824                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7825                         packageName, e.toString());
7826                 keyValueAgentErrorCleanup();    // clears any pending timeout messages as well
7827 
7828                 // After a restore failure we go back to running the queue.  If there
7829                 // are no more packages to be restored that will be handled by the
7830                 // next step.
7831                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7832             }
7833         }
7834 
7835         // state RESTORE_FULL : restore one package via streaming engine
restoreFull()7836         private void restoreFull() {
7837             // None of this can run on the work looper here, so we spin asynchronous
7838             // work like this:
7839             //
7840             //   StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
7841             //                       write it into the pipe to the engine
7842             //   EngineThread: FullRestoreEngine thread communicating with the target app
7843             //
7844             // When finished, StreamFeederThread executes next state as appropriate on the
7845             // backup looper, and the overall unified restore task resumes
7846             try {
7847                 StreamFeederThread feeder = new StreamFeederThread();
7848                 if (MORE_DEBUG) {
7849                     Slog.i(TAG, "Spinning threads for stream restore of "
7850                             + mCurrentPackage.packageName);
7851                 }
7852                 new Thread(feeder, "unified-stream-feeder").start();
7853 
7854                 // At this point the feeder is responsible for advancing the restore
7855                 // state, so we're done here.
7856             } catch (IOException e) {
7857                 // Unable to instantiate the feeder thread -- we need to bail on the
7858                 // current target.  We haven't asked the transport for data yet, though,
7859                 // so we can do that simply by going back to running the restore queue.
7860                 Slog.e(TAG, "Unable to construct pipes for stream restore!");
7861                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7862             }
7863         }
7864 
7865         // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end
restoreFinished()7866         private void restoreFinished() {
7867             try {
7868                 final int token = generateToken();
7869                 prepareOperationTimeout(token, TIMEOUT_RESTORE_FINISHED_INTERVAL, this);
7870                 mAgent.doRestoreFinished(token, mBackupManagerBinder);
7871                 // If we get this far, the callback or timeout will schedule the
7872                 // next restore state, so we're done
7873             } catch (Exception e) {
7874                 final String packageName = mCurrentPackage.packageName;
7875                 Slog.e(TAG, "Unable to finalize restore of " + packageName);
7876                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7877                         packageName, e.toString());
7878                 keyValueAgentErrorCleanup();
7879                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
7880             }
7881         }
7882 
7883         class StreamFeederThread extends RestoreEngine implements Runnable {
7884             final String TAG = "StreamFeederThread";
7885             FullRestoreEngine mEngine;
7886 
7887             // pipe through which we read data from the transport. [0] read, [1] write
7888             ParcelFileDescriptor[] mTransportPipes;
7889 
7890             // pipe through which the engine will read data.  [0] read, [1] write
7891             ParcelFileDescriptor[] mEnginePipes;
7892 
StreamFeederThread()7893             public StreamFeederThread() throws IOException {
7894                 mTransportPipes = ParcelFileDescriptor.createPipe();
7895                 mEnginePipes = ParcelFileDescriptor.createPipe();
7896                 setRunning(true);
7897             }
7898 
7899             @Override
run()7900             public void run() {
7901                 UnifiedRestoreState nextState = UnifiedRestoreState.RUNNING_QUEUE;
7902                 int status = BackupTransport.TRANSPORT_OK;
7903 
7904                 EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
7905                         mCurrentPackage.packageName);
7906 
7907                 mEngine = new FullRestoreEngine(null, mCurrentPackage, false, false);
7908                 EngineThread eThread = new EngineThread(mEngine, mEnginePipes[0]);
7909 
7910                 ParcelFileDescriptor eWriteEnd = mEnginePipes[1];
7911                 ParcelFileDescriptor tReadEnd = mTransportPipes[0];
7912                 ParcelFileDescriptor tWriteEnd = mTransportPipes[1];
7913 
7914                 int bufferSize = 32 * 1024;
7915                 byte[] buffer = new byte[bufferSize];
7916                 FileOutputStream engineOut = new FileOutputStream(eWriteEnd.getFileDescriptor());
7917                 FileInputStream transportIn = new FileInputStream(tReadEnd.getFileDescriptor());
7918 
7919                 // spin up the engine and start moving data to it
7920                 new Thread(eThread, "unified-restore-engine").start();
7921 
7922                 try {
7923                     while (status == BackupTransport.TRANSPORT_OK) {
7924                         // have the transport write some of the restoring data to us
7925                         int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
7926                         if (result > 0) {
7927                             // The transport wrote this many bytes of restore data to the
7928                             // pipe, so pass it along to the engine.
7929                             if (MORE_DEBUG) {
7930                                 Slog.v(TAG, "  <- transport provided chunk size " + result);
7931                             }
7932                             if (result > bufferSize) {
7933                                 bufferSize = result;
7934                                 buffer = new byte[bufferSize];
7935                             }
7936                             int toCopy = result;
7937                             while (toCopy > 0) {
7938                                 int n = transportIn.read(buffer, 0, toCopy);
7939                                 engineOut.write(buffer, 0, n);
7940                                 toCopy -= n;
7941                                 if (MORE_DEBUG) {
7942                                     Slog.v(TAG, "  -> wrote " + n + " to engine, left=" + toCopy);
7943                                 }
7944                             }
7945                         } else if (result == BackupTransport.NO_MORE_DATA) {
7946                             // Clean finish.  Wind up and we're done!
7947                             if (MORE_DEBUG) {
7948                                 Slog.i(TAG, "Got clean full-restore EOF for "
7949                                         + mCurrentPackage.packageName);
7950                             }
7951                             status = BackupTransport.TRANSPORT_OK;
7952                             break;
7953                         } else {
7954                             // Transport reported some sort of failure; the fall-through
7955                             // handling will deal properly with that.
7956                             Slog.e(TAG, "Error " + result + " streaming restore for "
7957                                     + mCurrentPackage.packageName);
7958                             EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7959                             status = result;
7960                         }
7961                     }
7962                     if (MORE_DEBUG) Slog.v(TAG, "Done copying to engine, falling through");
7963                 } catch (IOException e) {
7964                     // We lost our ability to communicate via the pipes.  That's worrying
7965                     // but potentially recoverable; abandon this package's restore but
7966                     // carry on with the next restore target.
7967                     Slog.e(TAG, "Unable to route data for restore");
7968                     EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
7969                             mCurrentPackage.packageName, "I/O error on pipes");
7970                     status = BackupTransport.AGENT_ERROR;
7971                 } catch (RemoteException e) {
7972                     // The transport went away; terminate the whole operation.  Closing
7973                     // the sockets will wake up the engine and it will then tidy up the
7974                     // remote end.
7975                     Slog.e(TAG, "Transport failed during restore");
7976                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
7977                     status = BackupTransport.TRANSPORT_ERROR;
7978                 } finally {
7979                     // Close the transport pipes and *our* end of the engine pipe,
7980                     // but leave the engine thread's end open so that it properly
7981                     // hits EOF and winds up its operations.
7982                     IoUtils.closeQuietly(mEnginePipes[1]);
7983                     IoUtils.closeQuietly(mTransportPipes[0]);
7984                     IoUtils.closeQuietly(mTransportPipes[1]);
7985 
7986                     // Don't proceed until the engine has finished
7987                     eThread.waitForResult();
7988 
7989                     if (MORE_DEBUG) {
7990                         Slog.i(TAG, "engine thread finished; proceeding");
7991                     }
7992 
7993                     // Now we're really done with this one too
7994                     IoUtils.closeQuietly(mEnginePipes[0]);
7995 
7996                     // If we hit a transport-level error, we are done with everything;
7997                     // if we hit an agent error we just go back to running the queue.
7998                     if (status == BackupTransport.TRANSPORT_OK) {
7999                         // Clean finish means we issue the restore-finished callback
8000                         nextState = UnifiedRestoreState.RESTORE_FINISHED;
8001 
8002                         // the engine bound the target's agent, so recover that binding
8003                         // to use for the callback.
8004                         mAgent = mEngine.getAgent();
8005                     } else {
8006                         // Something went wrong somewhere.  Whether it was at the transport
8007                         // level is immaterial; we need to tell the transport to bail
8008                         try {
8009                             mTransport.abortFullRestore();
8010                         } catch (RemoteException e) {
8011                             // transport itself is dead; make sure we handle this as a
8012                             // fatal error
8013                             status = BackupTransport.TRANSPORT_ERROR;
8014                         }
8015 
8016                         // We also need to wipe the current target's data, as it's probably
8017                         // in an incoherent state.
8018                         clearApplicationDataSynchronous(mCurrentPackage.packageName);
8019 
8020                         // Schedule the next state based on the nature of our failure
8021                         if (status == BackupTransport.TRANSPORT_ERROR) {
8022                             nextState = UnifiedRestoreState.FINAL;
8023                         } else {
8024                             nextState = UnifiedRestoreState.RUNNING_QUEUE;
8025                         }
8026                     }
8027                     executeNextState(nextState);
8028                     setRunning(false);
8029                 }
8030             }
8031 
8032         }
8033 
8034         class EngineThread implements Runnable {
8035             FullRestoreEngine mEngine;
8036             FileInputStream mEngineStream;
8037 
EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket)8038             EngineThread(FullRestoreEngine engine, ParcelFileDescriptor engineSocket) {
8039                 mEngine = engine;
8040                 engine.setRunning(true);
8041                 mEngineStream = new FileInputStream(engineSocket.getFileDescriptor());
8042             }
8043 
isRunning()8044             public boolean isRunning() {
8045                 return mEngine.isRunning();
8046             }
8047 
waitForResult()8048             public int waitForResult() {
8049                 return mEngine.waitForResult();
8050             }
8051 
8052             @Override
run()8053             public void run() {
8054                 while (mEngine.isRunning()) {
8055                     // Tell it to be sure to leave the agent instance up after finishing
8056                     mEngine.restoreOneFile(mEngineStream, false);
8057                 }
8058             }
8059         }
8060 
8061         // state FINAL : tear everything down and we're done.
finalizeRestore()8062         private void finalizeRestore() {
8063             if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
8064 
8065             try {
8066                 mTransport.finishRestore();
8067             } catch (Exception e) {
8068                 Slog.e(TAG, "Error finishing restore", e);
8069             }
8070 
8071             // Tell the observer we're done
8072             if (mObserver != null) {
8073                 try {
8074                     mObserver.restoreFinished(mStatus);
8075                 } catch (RemoteException e) {
8076                     Slog.d(TAG, "Restore observer died at restoreFinished");
8077                 }
8078             }
8079 
8080             // Clear any ongoing session timeout.
8081             mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
8082 
8083             // If we have a PM token, we must under all circumstances be sure to
8084             // handshake when we've finished.
8085             if (mPmToken > 0) {
8086                 if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
8087                 try {
8088                     mPackageManagerBinder.finishPackageInstall(mPmToken);
8089                 } catch (RemoteException e) { /* can't happen */ }
8090             } else {
8091                 // We were invoked via an active restore session, not by the Package
8092                 // Manager, so start up the session timeout again.
8093                 mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
8094                         TIMEOUT_RESTORE_INTERVAL);
8095             }
8096 
8097             // Kick off any work that may be needed regarding app widget restores
8098             AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER);
8099 
8100             // If this was a full-system restore, record the ancestral
8101             // dataset information
8102             if (mIsSystemRestore && mPmAgent != null) {
8103                 mAncestralPackages = mPmAgent.getRestoredPackages();
8104                 mAncestralToken = mToken;
8105                 writeRestoreTokens();
8106             }
8107 
8108             // done; we can finally release the wakelock and be legitimately done.
8109             Slog.i(TAG, "Restore complete.");
8110             mWakelock.release();
8111         }
8112 
keyValueAgentErrorCleanup()8113         void keyValueAgentErrorCleanup() {
8114             // If the agent fails restore, it might have put the app's data
8115             // into an incoherent state.  For consistency we wipe its data
8116             // again in this case before continuing with normal teardown
8117             clearApplicationDataSynchronous(mCurrentPackage.packageName);
8118             keyValueAgentCleanup();
8119         }
8120 
8121         // TODO: clean up naming; this is now used at finish by both k/v and stream restores
keyValueAgentCleanup()8122         void keyValueAgentCleanup() {
8123             mBackupDataName.delete();
8124             mStageName.delete();
8125             try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
8126             try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
8127             mBackupData = mNewState = null;
8128 
8129             // if everything went okay, remember the recorded state now
8130             //
8131             // !!! TODO: the restored data could be migrated on the server
8132             // side into the current dataset.  In that case the new state file
8133             // we just created would reflect the data already extant in the
8134             // backend, so there'd be nothing more to do.  Until that happens,
8135             // however, we need to make sure that we record the data to the
8136             // current backend dataset.  (Yes, this means shipping the data over
8137             // the wire in both directions.  That's bad, but consistency comes
8138             // first, then efficiency.)  Once we introduce server-side data
8139             // migration to the newly-restored device's dataset, we will change
8140             // the following from a discard of the newly-written state to the
8141             // "correct" operation of renaming into the canonical state blob.
8142             mNewStateName.delete();                      // TODO: remove; see above comment
8143             //mNewStateName.renameTo(mSavedStateName);   // TODO: replace with this
8144 
8145             // If this wasn't the PM pseudopackage, tear down the agent side
8146             if (mCurrentPackage.applicationInfo != null) {
8147                 // unbind and tidy up even on timeout or failure
8148                 try {
8149                     mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
8150 
8151                     // The agent was probably running with a stub Application object,
8152                     // which isn't a valid run mode for the main app logic.  Shut
8153                     // down the app so that next time it's launched, it gets the
8154                     // usual full initialization.  Note that this is only done for
8155                     // full-system restores: when a single app has requested a restore,
8156                     // it is explicitly not killed following that operation.
8157                     //
8158                     // We execute this kill when these conditions hold:
8159                     //    1. the app did not request its own restore (mTargetPackage == null), and either
8160                     //    2a. the app is a full-data target (TYPE_FULL_STREAM) or
8161                     //     b. the app does not state android:killAfterRestore="false" in its manifest
8162                     final int appFlags = mCurrentPackage.applicationInfo.flags;
8163                     final boolean killAfterRestore =
8164                             (mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
8165                             || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0);
8166 
8167                     if (mTargetPackage == null && killAfterRestore) {
8168                         if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
8169                                 + mCurrentPackage.applicationInfo.processName);
8170                         mActivityManager.killApplicationProcess(
8171                                 mCurrentPackage.applicationInfo.processName,
8172                                 mCurrentPackage.applicationInfo.uid);
8173                     }
8174                 } catch (RemoteException e) {
8175                     // can't happen; we run in the same process as the activity manager
8176                 }
8177             }
8178 
8179             // The caller is responsible for reestablishing the state machine; our
8180             // responsibility here is to clear the decks for whatever comes next.
8181             mBackupHandler.removeMessages(MSG_TIMEOUT, this);
8182             synchronized (mCurrentOpLock) {
8183                 mCurrentOperations.clear();
8184             }
8185         }
8186 
8187         @Override
operationComplete(int unusedResult)8188         public void operationComplete(int unusedResult) {
8189             if (MORE_DEBUG) {
8190                 Slog.i(TAG, "operationComplete() during restore: target="
8191                         + mCurrentPackage.packageName
8192                         + " state=" + mState);
8193             }
8194 
8195             final UnifiedRestoreState nextState;
8196             switch (mState) {
8197                 case INITIAL:
8198                     // We've just (manually) restored the PMBA.  It doesn't need the
8199                     // additional restore-finished callback so we bypass that and go
8200                     // directly to running the queue.
8201                     nextState = UnifiedRestoreState.RUNNING_QUEUE;
8202                     break;
8203 
8204                 case RESTORE_KEYVALUE:
8205                 case RESTORE_FULL: {
8206                     // Okay, we've just heard back from the agent that it's done with
8207                     // the restore itself.  We now have to send the same agent its
8208                     // doRestoreFinished() callback, so roll into that state.
8209                     nextState = UnifiedRestoreState.RESTORE_FINISHED;
8210                     break;
8211                 }
8212 
8213                 case RESTORE_FINISHED: {
8214                     // Okay, we're done with this package.  Tidy up and go on to the next
8215                     // app in the queue.
8216                     int size = (int) mBackupDataName.length();
8217                     EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
8218                             mCurrentPackage.packageName, size);
8219 
8220                     // Just go back to running the restore queue
8221                     keyValueAgentCleanup();
8222 
8223                     // If there was widget state associated with this app, get the OS to
8224                     // incorporate it into current bookeeping and then pass that along to
8225                     // the app as part of the restore-time work.
8226                     if (mWidgetData != null) {
8227                         restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
8228                     }
8229 
8230                     nextState = UnifiedRestoreState.RUNNING_QUEUE;
8231                     break;
8232                 }
8233 
8234                 default: {
8235                     // Some kind of horrible semantic error; we're in an unexpected state.
8236                     // Back off hard and wind up.
8237                     Slog.e(TAG, "Unexpected restore callback into state " + mState);
8238                     keyValueAgentErrorCleanup();
8239                     nextState = UnifiedRestoreState.FINAL;
8240                     break;
8241                 }
8242             }
8243 
8244             executeNextState(nextState);
8245         }
8246 
8247         // A call to agent.doRestore() or agent.doRestoreFinished() has timed out
8248         @Override
handleTimeout()8249         public void handleTimeout() {
8250             Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
8251             EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
8252                     mCurrentPackage.packageName, "restore timeout");
8253             // Handle like an agent that threw on invocation: wipe it and go on to the next
8254             keyValueAgentErrorCleanup();
8255             executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
8256         }
8257 
executeNextState(UnifiedRestoreState nextState)8258         void executeNextState(UnifiedRestoreState nextState) {
8259             if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
8260                     + this + " nextState=" + nextState);
8261             mState = nextState;
8262             Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
8263             mBackupHandler.sendMessage(msg);
8264         }
8265 
8266         // restore observer support
sendStartRestore(int numPackages)8267         void sendStartRestore(int numPackages) {
8268             if (mObserver != null) {
8269                 try {
8270                     mObserver.restoreStarting(numPackages);
8271                 } catch (RemoteException e) {
8272                     Slog.w(TAG, "Restore observer went away: startRestore");
8273                     mObserver = null;
8274                 }
8275             }
8276         }
8277 
sendOnRestorePackage(String name)8278         void sendOnRestorePackage(String name) {
8279             if (mObserver != null) {
8280                 if (mObserver != null) {
8281                     try {
8282                         mObserver.onUpdate(mCount, name);
8283                     } catch (RemoteException e) {
8284                         Slog.d(TAG, "Restore observer died in onUpdate");
8285                         mObserver = null;
8286                     }
8287                 }
8288             }
8289         }
8290 
sendEndRestore()8291         void sendEndRestore() {
8292             if (mObserver != null) {
8293                 try {
8294                     mObserver.restoreFinished(mStatus);
8295                 } catch (RemoteException e) {
8296                     Slog.w(TAG, "Restore observer went away: endRestore");
8297                     mObserver = null;
8298                 }
8299             }
8300         }
8301     }
8302 
8303     class PerformClearTask implements Runnable {
8304         IBackupTransport mTransport;
8305         PackageInfo mPackage;
8306 
PerformClearTask(IBackupTransport transport, PackageInfo packageInfo)8307         PerformClearTask(IBackupTransport transport, PackageInfo packageInfo) {
8308             mTransport = transport;
8309             mPackage = packageInfo;
8310         }
8311 
run()8312         public void run() {
8313             try {
8314                 // Clear the on-device backup state to ensure a full backup next time
8315                 File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
8316                 File stateFile = new File(stateDir, mPackage.packageName);
8317                 stateFile.delete();
8318 
8319                 // Tell the transport to remove all the persistent storage for the app
8320                 // TODO - need to handle failures
8321                 mTransport.clearBackupData(mPackage);
8322             } catch (RemoteException e) {
8323                 // can't happen; the transport is local
8324             } catch (Exception e) {
8325                 Slog.e(TAG, "Transport threw attempting to clear data for " + mPackage);
8326             } finally {
8327                 try {
8328                     // TODO - need to handle failures
8329                     mTransport.finishBackup();
8330                 } catch (RemoteException e) {
8331                     // can't happen; the transport is local
8332                 }
8333 
8334                 // Last but not least, release the cpu
8335                 mWakelock.release();
8336             }
8337         }
8338     }
8339 
8340     class PerformInitializeTask implements Runnable {
8341         HashSet<String> mQueue;
8342 
PerformInitializeTask(HashSet<String> transportNames)8343         PerformInitializeTask(HashSet<String> transportNames) {
8344             mQueue = transportNames;
8345         }
8346 
run()8347         public void run() {
8348             try {
8349                 for (String transportName : mQueue) {
8350                     IBackupTransport transport = getTransport(transportName);
8351                     if (transport == null) {
8352                         Slog.e(TAG, "Requested init for " + transportName + " but not found");
8353                         continue;
8354                     }
8355 
8356                     Slog.i(TAG, "Initializing (wiping) backup transport storage: " + transportName);
8357                     EventLog.writeEvent(EventLogTags.BACKUP_START, transport.transportDirName());
8358                     long startRealtime = SystemClock.elapsedRealtime();
8359                     int status = transport.initializeDevice();
8360 
8361                     if (status == BackupTransport.TRANSPORT_OK) {
8362                         status = transport.finishBackup();
8363                     }
8364 
8365                     // Okay, the wipe really happened.  Clean up our local bookkeeping.
8366                     if (status == BackupTransport.TRANSPORT_OK) {
8367                         Slog.i(TAG, "Device init successful");
8368                         int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
8369                         EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
8370                         resetBackupState(new File(mBaseStateDir, transport.transportDirName()));
8371                         EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
8372                         synchronized (mQueueLock) {
8373                             recordInitPendingLocked(false, transportName);
8374                         }
8375                     } else {
8376                         // If this didn't work, requeue this one and try again
8377                         // after a suitable interval
8378                         Slog.e(TAG, "Transport error in initializeDevice()");
8379                         EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
8380                         synchronized (mQueueLock) {
8381                             recordInitPendingLocked(true, transportName);
8382                         }
8383                         // do this via another alarm to make sure of the wakelock states
8384                         long delay = transport.requestBackupTime();
8385                         Slog.w(TAG, "Init failed on " + transportName + " resched in " + delay);
8386                         mAlarmManager.set(AlarmManager.RTC_WAKEUP,
8387                                 System.currentTimeMillis() + delay, mRunInitIntent);
8388                     }
8389                 }
8390             } catch (RemoteException e) {
8391                 // can't happen; the transports are local
8392             } catch (Exception e) {
8393                 Slog.e(TAG, "Unexpected error performing init", e);
8394             } finally {
8395                 // Done; release the wakelock
8396                 mWakelock.release();
8397             }
8398         }
8399     }
8400 
dataChangedImpl(String packageName)8401     private void dataChangedImpl(String packageName) {
8402         HashSet<String> targets = dataChangedTargets(packageName);
8403         dataChangedImpl(packageName, targets);
8404     }
8405 
dataChangedImpl(String packageName, HashSet<String> targets)8406     private void dataChangedImpl(String packageName, HashSet<String> targets) {
8407         // Record that we need a backup pass for the caller.  Since multiple callers
8408         // may share a uid, we need to note all candidates within that uid and schedule
8409         // a backup pass for each of them.
8410         if (targets == null) {
8411             Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8412                    + " uid=" + Binder.getCallingUid());
8413             return;
8414         }
8415 
8416         synchronized (mQueueLock) {
8417             // Note that this client has made data changes that need to be backed up
8418             if (targets.contains(packageName)) {
8419                 // Add the caller to the set of pending backups.  If there is
8420                 // one already there, then overwrite it, but no harm done.
8421                 BackupRequest req = new BackupRequest(packageName);
8422                 if (mPendingBackups.put(packageName, req) == null) {
8423                     if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
8424 
8425                     // Journal this request in case of crash.  The put()
8426                     // operation returned null when this package was not already
8427                     // in the set; we want to avoid touching the disk redundantly.
8428                     writeToJournalLocked(packageName);
8429                 }
8430             }
8431         }
8432 
8433         // ...and schedule a backup pass if necessary
8434         KeyValueBackupJob.schedule(mContext);
8435     }
8436 
8437     // Note: packageName is currently unused, but may be in the future
dataChangedTargets(String packageName)8438     private HashSet<String> dataChangedTargets(String packageName) {
8439         // If the caller does not hold the BACKUP permission, it can only request a
8440         // backup of its own data.
8441         if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8442                 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8443             synchronized (mBackupParticipants) {
8444                 return mBackupParticipants.get(Binder.getCallingUid());
8445             }
8446         }
8447 
8448         // a caller with full permission can ask to back up any participating app
8449         HashSet<String> targets = new HashSet<String>();
8450         if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
8451             targets.add(PACKAGE_MANAGER_SENTINEL);
8452         } else {
8453             synchronized (mBackupParticipants) {
8454                 int N = mBackupParticipants.size();
8455                 for (int i = 0; i < N; i++) {
8456                     HashSet<String> s = mBackupParticipants.valueAt(i);
8457                     if (s != null) {
8458                         targets.addAll(s);
8459                     }
8460                 }
8461             }
8462         }
8463         return targets;
8464     }
8465 
writeToJournalLocked(String str)8466     private void writeToJournalLocked(String str) {
8467         RandomAccessFile out = null;
8468         try {
8469             if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
8470             out = new RandomAccessFile(mJournal, "rws");
8471             out.seek(out.length());
8472             out.writeUTF(str);
8473         } catch (IOException e) {
8474             Slog.e(TAG, "Can't write " + str + " to backup journal", e);
8475             mJournal = null;
8476         } finally {
8477             try { if (out != null) out.close(); } catch (IOException e) {}
8478         }
8479     }
8480 
8481     // ----- IBackupManager binder interface -----
8482 
dataChanged(final String packageName)8483     public void dataChanged(final String packageName) {
8484         final int callingUserHandle = UserHandle.getCallingUserId();
8485         if (callingUserHandle != UserHandle.USER_OWNER) {
8486             // App is running under a non-owner user profile.  For now, we do not back
8487             // up data from secondary user profiles.
8488             // TODO: backups for all user profiles.
8489             if (MORE_DEBUG) {
8490                 Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
8491                         + callingUserHandle);
8492             }
8493             return;
8494         }
8495 
8496         final HashSet<String> targets = dataChangedTargets(packageName);
8497         if (targets == null) {
8498             Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
8499                    + " uid=" + Binder.getCallingUid());
8500             return;
8501         }
8502 
8503         mBackupHandler.post(new Runnable() {
8504                 public void run() {
8505                     dataChangedImpl(packageName, targets);
8506                 }
8507             });
8508     }
8509 
8510     // Clear the given package's backup data from the current transport
clearBackupData(String transportName, String packageName)8511     public void clearBackupData(String transportName, String packageName) {
8512         if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
8513         PackageInfo info;
8514         try {
8515             info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
8516         } catch (NameNotFoundException e) {
8517             Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
8518             return;
8519         }
8520 
8521         // If the caller does not hold the BACKUP permission, it can only request a
8522         // wipe of its own backed-up data.
8523         HashSet<String> apps;
8524         if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
8525                 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
8526             apps = mBackupParticipants.get(Binder.getCallingUid());
8527         } else {
8528             // a caller with full permission can ask to back up any participating app
8529             // !!! TODO: allow data-clear of ANY app?
8530             if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
8531             apps = new HashSet<String>();
8532             int N = mBackupParticipants.size();
8533             for (int i = 0; i < N; i++) {
8534                 HashSet<String> s = mBackupParticipants.valueAt(i);
8535                 if (s != null) {
8536                     apps.addAll(s);
8537                 }
8538             }
8539         }
8540 
8541         // Is the given app an available participant?
8542         if (apps.contains(packageName)) {
8543             // found it; fire off the clear request
8544             if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
8545             mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
8546             synchronized (mQueueLock) {
8547                 final IBackupTransport transport = getTransport(transportName);
8548                 if (transport == null) {
8549                     // transport is currently unavailable -- make sure to retry
8550                     Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
8551                             new ClearRetryParams(transportName, packageName));
8552                     mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
8553                     return;
8554                 }
8555                 long oldId = Binder.clearCallingIdentity();
8556                 mWakelock.acquire();
8557                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
8558                         new ClearParams(transport, info));
8559                 mBackupHandler.sendMessage(msg);
8560                 Binder.restoreCallingIdentity(oldId);
8561             }
8562         }
8563     }
8564 
8565     // Run a backup pass immediately for any applications that have declared
8566     // that they have pending updates.
backupNow()8567     public void backupNow() {
8568         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
8569 
8570         if (mPowerManager.isPowerSaveMode()) {
8571             if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
8572             KeyValueBackupJob.schedule(mContext);   // try again in several hours
8573         } else {
8574             if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
8575             synchronized (mQueueLock) {
8576                 // Fire the intent that kicks off the whole shebang...
8577                 try {
8578                     mRunBackupIntent.send();
8579                 } catch (PendingIntent.CanceledException e) {
8580                     // should never happen
8581                     Slog.e(TAG, "run-backup intent cancelled!");
8582                 }
8583 
8584                 // ...and cancel any pending scheduled job, because we've just superseded it
8585                 KeyValueBackupJob.cancel(mContext);
8586             }
8587         }
8588     }
8589 
deviceIsProvisioned()8590     boolean deviceIsProvisioned() {
8591         final ContentResolver resolver = mContext.getContentResolver();
8592         return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
8593     }
8594 
8595     // Run a *full* backup pass for the given packages, writing the resulting data stream
8596     // to the supplied file descriptor.  This method is synchronous and does not return
8597     // to the caller until the backup has been completed.
8598     //
8599     // This is the variant used by 'adb backup'; it requires on-screen confirmation
8600     // by the user because it can be used to offload data over untrusted USB.
fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList)8601     public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
8602             boolean includeObbs, boolean includeShared, boolean doWidgets,
8603             boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
8604         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
8605 
8606         final int callingUserHandle = UserHandle.getCallingUserId();
8607         if (callingUserHandle != UserHandle.USER_OWNER) {
8608             throw new IllegalStateException("Backup supported only for the device owner");
8609         }
8610 
8611         // Validate
8612         if (!doAllApps) {
8613             if (!includeShared) {
8614                 // If we're backing up shared data (sdcard or equivalent), then we can run
8615                 // without any supplied app names.  Otherwise, we'd be doing no work, so
8616                 // report the error.
8617                 if (pkgList == null || pkgList.length == 0) {
8618                     throw new IllegalArgumentException(
8619                             "Backup requested but neither shared nor any apps named");
8620                 }
8621             }
8622         }
8623 
8624         long oldId = Binder.clearCallingIdentity();
8625         try {
8626             // Doesn't make sense to do a full backup prior to setup
8627             if (!deviceIsProvisioned()) {
8628                 Slog.i(TAG, "Full backup not supported before setup");
8629                 return;
8630             }
8631 
8632             if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
8633                     + " obb=" + includeObbs + " shared=" + includeShared + " all=" + doAllApps
8634                     + " system=" + includeSystem + " pkgs=" + pkgList);
8635             Slog.i(TAG, "Beginning full backup...");
8636 
8637             FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
8638                     includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
8639             final int token = generateToken();
8640             synchronized (mFullConfirmations) {
8641                 mFullConfirmations.put(token, params);
8642             }
8643 
8644             // start up the confirmation UI
8645             if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
8646             if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
8647                 Slog.e(TAG, "Unable to launch full backup confirmation");
8648                 mFullConfirmations.delete(token);
8649                 return;
8650             }
8651 
8652             // make sure the screen is lit for the user interaction
8653             mPowerManager.userActivity(SystemClock.uptimeMillis(),
8654                     PowerManager.USER_ACTIVITY_EVENT_OTHER,
8655                     0);
8656 
8657             // start the confirmation countdown
8658             startConfirmationTimeout(token, params);
8659 
8660             // wait for the backup to be performed
8661             if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
8662             waitForCompletion(params);
8663         } finally {
8664             try {
8665                 fd.close();
8666             } catch (IOException e) {
8667                 // just eat it
8668             }
8669             Binder.restoreCallingIdentity(oldId);
8670             Slog.d(TAG, "Full backup processing complete.");
8671         }
8672     }
8673 
fullTransportBackup(String[] pkgNames)8674     public void fullTransportBackup(String[] pkgNames) {
8675         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
8676                 "fullTransportBackup");
8677 
8678         final int callingUserHandle = UserHandle.getCallingUserId();
8679         if (callingUserHandle != UserHandle.USER_OWNER) {
8680             throw new IllegalStateException("Restore supported only for the device owner");
8681         }
8682 
8683         if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
8684             Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
8685         } else {
8686             if (DEBUG) {
8687                 Slog.d(TAG, "fullTransportBackup()");
8688             }
8689 
8690             CountDownLatch latch = new CountDownLatch(1);
8691             PerformFullTransportBackupTask task =
8692                     new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
8693             (new Thread(task, "full-transport-master")).start();
8694             do {
8695                 try {
8696                     latch.await();
8697                     break;
8698                 } catch (InterruptedException e) {
8699                     // Just go back to waiting for the latch to indicate completion
8700                 }
8701             } while (true);
8702 
8703             // We just ran a backup on these packages, so kick them to the end of the queue
8704             final long now = System.currentTimeMillis();
8705             for (String pkg : pkgNames) {
8706                 enqueueFullBackup(pkg, now);
8707             }
8708         }
8709 
8710         if (DEBUG) {
8711             Slog.d(TAG, "Done with full transport backup.");
8712         }
8713     }
8714 
fullRestore(ParcelFileDescriptor fd)8715     public void fullRestore(ParcelFileDescriptor fd) {
8716         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
8717 
8718         final int callingUserHandle = UserHandle.getCallingUserId();
8719         if (callingUserHandle != UserHandle.USER_OWNER) {
8720             throw new IllegalStateException("Restore supported only for the device owner");
8721         }
8722 
8723         long oldId = Binder.clearCallingIdentity();
8724 
8725         try {
8726             // Check whether the device has been provisioned -- we don't handle
8727             // full restores prior to completing the setup process.
8728             if (!deviceIsProvisioned()) {
8729                 Slog.i(TAG, "Full restore not permitted before setup");
8730                 return;
8731             }
8732 
8733             Slog.i(TAG, "Beginning full restore...");
8734 
8735             FullRestoreParams params = new FullRestoreParams(fd);
8736             final int token = generateToken();
8737             synchronized (mFullConfirmations) {
8738                 mFullConfirmations.put(token, params);
8739             }
8740 
8741             // start up the confirmation UI
8742             if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
8743             if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
8744                 Slog.e(TAG, "Unable to launch full restore confirmation");
8745                 mFullConfirmations.delete(token);
8746                 return;
8747             }
8748 
8749             // make sure the screen is lit for the user interaction
8750             mPowerManager.userActivity(SystemClock.uptimeMillis(),
8751                     PowerManager.USER_ACTIVITY_EVENT_OTHER,
8752                     0);
8753 
8754             // start the confirmation countdown
8755             startConfirmationTimeout(token, params);
8756 
8757             // wait for the restore to be performed
8758             if (DEBUG) Slog.d(TAG, "Waiting for full restore completion...");
8759             waitForCompletion(params);
8760         } finally {
8761             try {
8762                 fd.close();
8763             } catch (IOException e) {
8764                 Slog.w(TAG, "Error trying to close fd after full restore: " + e);
8765             }
8766             Binder.restoreCallingIdentity(oldId);
8767             Slog.i(TAG, "Full restore processing complete.");
8768         }
8769     }
8770 
startConfirmationUi(int token, String action)8771     boolean startConfirmationUi(int token, String action) {
8772         try {
8773             Intent confIntent = new Intent(action);
8774             confIntent.setClassName("com.android.backupconfirm",
8775                     "com.android.backupconfirm.BackupRestoreConfirmation");
8776             confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
8777             confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
8778             mContext.startActivity(confIntent);
8779         } catch (ActivityNotFoundException e) {
8780             return false;
8781         }
8782         return true;
8783     }
8784 
startConfirmationTimeout(int token, FullParams params)8785     void startConfirmationTimeout(int token, FullParams params) {
8786         if (MORE_DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
8787                 + TIMEOUT_FULL_CONFIRMATION + " millis");
8788         Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
8789                 token, 0, params);
8790         mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
8791     }
8792 
waitForCompletion(FullParams params)8793     void waitForCompletion(FullParams params) {
8794         synchronized (params.latch) {
8795             while (params.latch.get() == false) {
8796                 try {
8797                     params.latch.wait();
8798                 } catch (InterruptedException e) { /* never interrupted */ }
8799             }
8800         }
8801     }
8802 
signalFullBackupRestoreCompletion(FullParams params)8803     void signalFullBackupRestoreCompletion(FullParams params) {
8804         synchronized (params.latch) {
8805             params.latch.set(true);
8806             params.latch.notifyAll();
8807         }
8808     }
8809 
8810     // Confirm that the previously-requested full backup/restore operation can proceed.  This
8811     // is used to require a user-facing disclosure about the operation.
acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword, String encPpassword, IFullBackupRestoreObserver observer)8812     public void acknowledgeFullBackupOrRestore(int token, boolean allow,
8813             String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
8814         if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
8815                 + " allow=" + allow);
8816 
8817         // TODO: possibly require not just this signature-only permission, but even
8818         // require that the specific designated confirmation-UI app uid is the caller?
8819         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "acknowledgeFullBackupOrRestore");
8820 
8821         long oldId = Binder.clearCallingIdentity();
8822         try {
8823 
8824             FullParams params;
8825             synchronized (mFullConfirmations) {
8826                 params = mFullConfirmations.get(token);
8827                 if (params != null) {
8828                     mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
8829                     mFullConfirmations.delete(token);
8830 
8831                     if (allow) {
8832                         final int verb = params instanceof FullBackupParams
8833                                 ? MSG_RUN_ADB_BACKUP
8834                                 : MSG_RUN_ADB_RESTORE;
8835 
8836                         params.observer = observer;
8837                         params.curPassword = curPassword;
8838 
8839                         params.encryptPassword = encPpassword;
8840 
8841                         if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
8842                         mWakelock.acquire();
8843                         Message msg = mBackupHandler.obtainMessage(verb, params);
8844                         mBackupHandler.sendMessage(msg);
8845                     } else {
8846                         Slog.w(TAG, "User rejected full backup/restore operation");
8847                         // indicate completion without having actually transferred any data
8848                         signalFullBackupRestoreCompletion(params);
8849                     }
8850                 } else {
8851                     Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
8852                 }
8853             }
8854         } finally {
8855             Binder.restoreCallingIdentity(oldId);
8856         }
8857     }
8858 
8859     // Enable/disable backups
setBackupEnabled(boolean enable)8860     public void setBackupEnabled(boolean enable) {
8861         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8862                 "setBackupEnabled");
8863 
8864         Slog.i(TAG, "Backup enabled => " + enable);
8865 
8866         long oldId = Binder.clearCallingIdentity();
8867         try {
8868             boolean wasEnabled = mEnabled;
8869             synchronized (this) {
8870                 Settings.Secure.putInt(mContext.getContentResolver(),
8871                         Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
8872                 mEnabled = enable;
8873             }
8874 
8875             synchronized (mQueueLock) {
8876                 if (enable && !wasEnabled && mProvisioned) {
8877                     // if we've just been enabled, start scheduling backup passes
8878                     KeyValueBackupJob.schedule(mContext);
8879                     scheduleNextFullBackupJob(0);
8880                 } else if (!enable) {
8881                     // No longer enabled, so stop running backups
8882                     if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
8883 
8884                     KeyValueBackupJob.cancel(mContext);
8885 
8886                     // This also constitutes an opt-out, so we wipe any data for
8887                     // this device from the backend.  We start that process with
8888                     // an alarm in order to guarantee wakelock states.
8889                     if (wasEnabled && mProvisioned) {
8890                         // NOTE: we currently flush every registered transport, not just
8891                         // the currently-active one.
8892                         HashSet<String> allTransports;
8893                         synchronized (mTransports) {
8894                             allTransports = new HashSet<String>(mTransports.keySet());
8895                         }
8896                         // build the set of transports for which we are posting an init
8897                         for (String transport : allTransports) {
8898                             recordInitPendingLocked(true, transport);
8899                         }
8900                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
8901                                 mRunInitIntent);
8902                     }
8903                 }
8904             }
8905         } finally {
8906             Binder.restoreCallingIdentity(oldId);
8907         }
8908     }
8909 
8910     // Enable/disable automatic restore of app data at install time
setAutoRestore(boolean doAutoRestore)8911     public void setAutoRestore(boolean doAutoRestore) {
8912         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8913                 "setAutoRestore");
8914 
8915         Slog.i(TAG, "Auto restore => " + doAutoRestore);
8916 
8917         final long oldId = Binder.clearCallingIdentity();
8918         try {
8919             synchronized (this) {
8920                 Settings.Secure.putInt(mContext.getContentResolver(),
8921                         Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
8922                 mAutoRestore = doAutoRestore;
8923             }
8924         } finally {
8925             Binder.restoreCallingIdentity(oldId);
8926         }
8927     }
8928 
8929     // Mark the backup service as having been provisioned
setBackupProvisioned(boolean available)8930     public void setBackupProvisioned(boolean available) {
8931         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8932                 "setBackupProvisioned");
8933         /*
8934          * This is now a no-op; provisioning is simply the device's own setup state.
8935          */
8936     }
8937 
8938     // Report whether the backup mechanism is currently enabled
isBackupEnabled()8939     public boolean isBackupEnabled() {
8940         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
8941         return mEnabled;    // no need to synchronize just to read it
8942     }
8943 
8944     // Report the name of the currently active transport
getCurrentTransport()8945     public String getCurrentTransport() {
8946         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8947                 "getCurrentTransport");
8948         if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
8949         return mCurrentTransport;
8950     }
8951 
8952     // Report all known, available backup transports
listAllTransports()8953     public String[] listAllTransports() {
8954         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "listAllTransports");
8955 
8956         String[] list = null;
8957         ArrayList<String> known = new ArrayList<String>();
8958         for (Map.Entry<String, IBackupTransport> entry : mTransports.entrySet()) {
8959             if (entry.getValue() != null) {
8960                 known.add(entry.getKey());
8961             }
8962         }
8963 
8964         if (known.size() > 0) {
8965             list = new String[known.size()];
8966             known.toArray(list);
8967         }
8968         return list;
8969     }
8970 
8971     // Select which transport to use for the next backup operation.
selectBackupTransport(String transport)8972     public String selectBackupTransport(String transport) {
8973         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8974                 "selectBackupTransport");
8975 
8976         synchronized (mTransports) {
8977             final long oldId = Binder.clearCallingIdentity();
8978             try {
8979                 String prevTransport = mCurrentTransport;
8980                 mCurrentTransport = transport;
8981                 Settings.Secure.putString(mContext.getContentResolver(),
8982                         Settings.Secure.BACKUP_TRANSPORT, transport);
8983                 Slog.v(TAG, "selectBackupTransport() set " + mCurrentTransport
8984                         + " returning " + prevTransport);
8985                 return prevTransport;
8986             } finally {
8987                 Binder.restoreCallingIdentity(oldId);
8988             }
8989         }
8990     }
8991 
8992     // Supply the configuration Intent for the given transport.  If the name is not one
8993     // of the available transports, or if the transport does not supply any configuration
8994     // UI, the method returns null.
getConfigurationIntent(String transportName)8995     public Intent getConfigurationIntent(String transportName) {
8996         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
8997                 "getConfigurationIntent");
8998 
8999         synchronized (mTransports) {
9000             final IBackupTransport transport = mTransports.get(transportName);
9001             if (transport != null) {
9002                 try {
9003                     final Intent intent = transport.configurationIntent();
9004                     if (MORE_DEBUG) Slog.d(TAG, "getConfigurationIntent() returning config intent "
9005                             + intent);
9006                     return intent;
9007                 } catch (RemoteException e) {
9008                     /* fall through to return null */
9009                 }
9010             }
9011         }
9012 
9013         return null;
9014     }
9015 
9016     // Supply the configuration summary string for the given transport.  If the name is
9017     // not one of the available transports, or if the transport does not supply any
9018     // summary / destination string, the method can return null.
9019     //
9020     // This string is used VERBATIM as the summary text of the relevant Settings item!
getDestinationString(String transportName)9021     public String getDestinationString(String transportName) {
9022         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9023                 "getDestinationString");
9024 
9025         synchronized (mTransports) {
9026             final IBackupTransport transport = mTransports.get(transportName);
9027             if (transport != null) {
9028                 try {
9029                     final String text = transport.currentDestinationString();
9030                     if (MORE_DEBUG) Slog.d(TAG, "getDestinationString() returning " + text);
9031                     return text;
9032                 } catch (RemoteException e) {
9033                     /* fall through to return null */
9034                 }
9035             }
9036         }
9037 
9038         return null;
9039     }
9040 
9041     // Supply the manage-data intent for the given transport.
getDataManagementIntent(String transportName)9042     public Intent getDataManagementIntent(String transportName) {
9043         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9044                 "getDataManagementIntent");
9045 
9046         synchronized (mTransports) {
9047             final IBackupTransport transport = mTransports.get(transportName);
9048             if (transport != null) {
9049                 try {
9050                     final Intent intent = transport.dataManagementIntent();
9051                     if (MORE_DEBUG) Slog.d(TAG, "getDataManagementIntent() returning intent "
9052                             + intent);
9053                     return intent;
9054                 } catch (RemoteException e) {
9055                     /* fall through to return null */
9056                 }
9057             }
9058         }
9059 
9060         return null;
9061     }
9062 
9063     // Supply the menu label for affordances that fire the manage-data intent
9064     // for the given transport.
getDataManagementLabel(String transportName)9065     public String getDataManagementLabel(String transportName) {
9066         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9067                 "getDataManagementLabel");
9068 
9069         synchronized (mTransports) {
9070             final IBackupTransport transport = mTransports.get(transportName);
9071             if (transport != null) {
9072                 try {
9073                     final String text = transport.dataManagementLabel();
9074                     if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
9075                     return text;
9076                 } catch (RemoteException e) {
9077                     /* fall through to return null */
9078                 }
9079             }
9080         }
9081 
9082         return null;
9083     }
9084 
9085     // Callback: a requested backup agent has been instantiated.  This should only
9086     // be called from the Activity Manager.
agentConnected(String packageName, IBinder agentBinder)9087     public void agentConnected(String packageName, IBinder agentBinder) {
9088         synchronized(mAgentConnectLock) {
9089             if (Binder.getCallingUid() == Process.SYSTEM_UID) {
9090                 Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
9091                 IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
9092                 mConnectedAgent = agent;
9093                 mConnecting = false;
9094             } else {
9095                 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9096                         + " claiming agent connected");
9097             }
9098             mAgentConnectLock.notifyAll();
9099         }
9100     }
9101 
9102     // Callback: a backup agent has failed to come up, or has unexpectedly quit.
9103     // If the agent failed to come up in the first place, the agentBinder argument
9104     // will be null.  This should only be called from the Activity Manager.
agentDisconnected(String packageName)9105     public void agentDisconnected(String packageName) {
9106         // TODO: handle backup being interrupted
9107         synchronized(mAgentConnectLock) {
9108             if (Binder.getCallingUid() == Process.SYSTEM_UID) {
9109                 mConnectedAgent = null;
9110                 mConnecting = false;
9111             } else {
9112                 Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9113                         + " claiming agent disconnected");
9114             }
9115             mAgentConnectLock.notifyAll();
9116         }
9117     }
9118 
9119     // An application being installed will need a restore pass, then the Package Manager
9120     // will need to be told when the restore is finished.
restoreAtInstall(String packageName, int token)9121     public void restoreAtInstall(String packageName, int token) {
9122         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
9123             Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
9124                     + " attemping install-time restore");
9125             return;
9126         }
9127 
9128         boolean skip = false;
9129 
9130         long restoreSet = getAvailableRestoreToken(packageName);
9131         if (DEBUG) Slog.v(TAG, "restoreAtInstall pkg=" + packageName
9132                 + " token=" + Integer.toHexString(token)
9133                 + " restoreSet=" + Long.toHexString(restoreSet));
9134         if (restoreSet == 0) {
9135             if (MORE_DEBUG) Slog.i(TAG, "No restore set");
9136             skip = true;
9137         }
9138 
9139         // Do we have a transport to fetch data for us?
9140         IBackupTransport transport = getTransport(mCurrentTransport);
9141         if (transport == null) {
9142             if (DEBUG) Slog.w(TAG, "No transport");
9143             skip = true;
9144         }
9145 
9146         if (!mAutoRestore) {
9147             if (DEBUG) {
9148                 Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
9149             }
9150             skip = true;
9151         }
9152 
9153         if (!skip) {
9154             try {
9155                 // okay, we're going to attempt a restore of this package from this restore set.
9156                 // The eventual message back into the Package Manager to run the post-install
9157                 // steps for 'token' will be issued from the restore handling code.
9158 
9159                 // This can throw and so *must* happen before the wakelock is acquired
9160                 String dirName = transport.transportDirName();
9161 
9162                 mWakelock.acquire();
9163                 if (MORE_DEBUG) {
9164                     Slog.d(TAG, "Restore at install of " + packageName);
9165                 }
9166                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9167                 msg.obj = new RestoreParams(transport, dirName, null,
9168                         restoreSet, packageName, token);
9169                 mBackupHandler.sendMessage(msg);
9170             } catch (RemoteException e) {
9171                 // Binding to the transport broke; back off and proceed with the installation.
9172                 Slog.e(TAG, "Unable to contact transport");
9173                 skip = true;
9174             }
9175         }
9176 
9177         if (skip) {
9178             // Auto-restore disabled or no way to attempt a restore; just tell the Package
9179             // Manager to proceed with the post-install handling for this package.
9180             if (DEBUG) Slog.v(TAG, "Finishing install immediately");
9181             try {
9182                 mPackageManagerBinder.finishPackageInstall(token);
9183             } catch (RemoteException e) { /* can't happen */ }
9184         }
9185     }
9186 
9187     // Hand off a restore session
beginRestoreSession(String packageName, String transport)9188     public IRestoreSession beginRestoreSession(String packageName, String transport) {
9189         if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
9190                 + " transport=" + transport);
9191 
9192         boolean needPermission = true;
9193         if (transport == null) {
9194             transport = mCurrentTransport;
9195 
9196             if (packageName != null) {
9197                 PackageInfo app = null;
9198                 try {
9199                     app = mPackageManager.getPackageInfo(packageName, 0);
9200                 } catch (NameNotFoundException nnf) {
9201                     Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9202                     throw new IllegalArgumentException("Package " + packageName + " not found");
9203                 }
9204 
9205                 if (app.applicationInfo.uid == Binder.getCallingUid()) {
9206                     // So: using the current active transport, and the caller has asked
9207                     // that its own package will be restored.  In this narrow use case
9208                     // we do not require the caller to hold the permission.
9209                     needPermission = false;
9210                 }
9211             }
9212         }
9213 
9214         if (needPermission) {
9215             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9216                     "beginRestoreSession");
9217         } else {
9218             if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
9219         }
9220 
9221         synchronized(this) {
9222             if (mActiveRestoreSession != null) {
9223                 Slog.d(TAG, "Restore session requested but one already active");
9224                 return null;
9225             }
9226             mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
9227             mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
9228         }
9229         return mActiveRestoreSession;
9230     }
9231 
clearRestoreSession(ActiveRestoreSession currentSession)9232     void clearRestoreSession(ActiveRestoreSession currentSession) {
9233         synchronized(this) {
9234             if (currentSession != mActiveRestoreSession) {
9235                 Slog.e(TAG, "ending non-current restore session");
9236             } else {
9237                 if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
9238                 mActiveRestoreSession = null;
9239                 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9240             }
9241         }
9242     }
9243 
9244     // Note that a currently-active backup agent has notified us that it has
9245     // completed the given outstanding asynchronous backup/restore operation.
opComplete(int token, long result)9246     public void opComplete(int token, long result) {
9247         if (MORE_DEBUG) {
9248             Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
9249         }
9250         Operation op = null;
9251         synchronized (mCurrentOpLock) {
9252             op = mCurrentOperations.get(token);
9253             if (op != null) {
9254                 op.state = OP_ACKNOWLEDGED;
9255             }
9256             mCurrentOpLock.notifyAll();
9257         }
9258 
9259         // The completion callback, if any, is invoked on the handler
9260         if (op != null && op.callback != null) {
9261             Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
9262             // NB: this cannot distinguish between results > 2 gig
9263             msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
9264             mBackupHandler.sendMessage(msg);
9265         }
9266     }
9267 
9268     // ----- Restore session -----
9269 
9270     class ActiveRestoreSession extends IRestoreSession.Stub {
9271         private static final String TAG = "RestoreSession";
9272 
9273         private String mPackageName;
9274         private IBackupTransport mRestoreTransport = null;
9275         RestoreSet[] mRestoreSets = null;
9276         boolean mEnded = false;
9277         boolean mTimedOut = false;
9278 
ActiveRestoreSession(String packageName, String transport)9279         ActiveRestoreSession(String packageName, String transport) {
9280             mPackageName = packageName;
9281             mRestoreTransport = getTransport(transport);
9282         }
9283 
markTimedOut()9284         public void markTimedOut() {
9285             mTimedOut = true;
9286         }
9287 
9288         // --- Binder interface ---
getAvailableRestoreSets(IRestoreObserver observer)9289         public synchronized int getAvailableRestoreSets(IRestoreObserver observer) {
9290             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9291                     "getAvailableRestoreSets");
9292             if (observer == null) {
9293                 throw new IllegalArgumentException("Observer must not be null");
9294             }
9295 
9296             if (mEnded) {
9297                 throw new IllegalStateException("Restore session already ended");
9298             }
9299 
9300             if (mTimedOut) {
9301                 Slog.i(TAG, "Session already timed out");
9302                 return -1;
9303             }
9304 
9305             long oldId = Binder.clearCallingIdentity();
9306             try {
9307                 if (mRestoreTransport == null) {
9308                     Slog.w(TAG, "Null transport getting restore sets");
9309                     return -1;
9310                 }
9311 
9312                 // We know we're doing legit work now, so halt the timeout
9313                 // until we're done.  It gets started again when the result
9314                 // comes in.
9315                 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9316 
9317                 // spin off the transport request to our service thread
9318                 mWakelock.acquire();
9319                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_GET_RESTORE_SETS,
9320                         new RestoreGetSetsParams(mRestoreTransport, this, observer));
9321                 mBackupHandler.sendMessage(msg);
9322                 return 0;
9323             } catch (Exception e) {
9324                 Slog.e(TAG, "Error in getAvailableRestoreSets", e);
9325                 return -1;
9326             } finally {
9327                 Binder.restoreCallingIdentity(oldId);
9328             }
9329         }
9330 
restoreAll(long token, IRestoreObserver observer)9331         public synchronized int restoreAll(long token, IRestoreObserver observer) {
9332             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9333                     "performRestore");
9334 
9335             if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
9336                     + " observer=" + observer);
9337 
9338             if (mEnded) {
9339                 throw new IllegalStateException("Restore session already ended");
9340             }
9341 
9342             if (mTimedOut) {
9343                 Slog.i(TAG, "Session already timed out");
9344                 return -1;
9345             }
9346 
9347             if (mRestoreTransport == null || mRestoreSets == null) {
9348                 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9349                 return -1;
9350             }
9351 
9352             if (mPackageName != null) {
9353                 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9354                 return -1;
9355             }
9356 
9357             String dirName;
9358             try {
9359                 dirName = mRestoreTransport.transportDirName();
9360             } catch (RemoteException e) {
9361                 // Transport went AWOL; fail.
9362                 Slog.e(TAG, "Unable to contact transport for restore");
9363                 return -1;
9364             }
9365 
9366             synchronized (mQueueLock) {
9367                 for (int i = 0; i < mRestoreSets.length; i++) {
9368                     if (token == mRestoreSets[i].token) {
9369                         // Real work, so stop the session timeout until we finalize the restore
9370                         mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9371 
9372                         long oldId = Binder.clearCallingIdentity();
9373                         mWakelock.acquire();
9374                         if (MORE_DEBUG) {
9375                             Slog.d(TAG, "restoreAll() kicking off");
9376                         }
9377                         Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9378                         msg.obj = new RestoreParams(mRestoreTransport, dirName,
9379                                 observer, token);
9380                         mBackupHandler.sendMessage(msg);
9381                         Binder.restoreCallingIdentity(oldId);
9382                         return 0;
9383                     }
9384                 }
9385             }
9386 
9387             Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9388             return -1;
9389         }
9390 
9391         // Restores of more than a single package are treated as 'system' restores
restoreSome(long token, IRestoreObserver observer, String[] packages)9392         public synchronized int restoreSome(long token, IRestoreObserver observer,
9393                 String[] packages) {
9394             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
9395                     "performRestore");
9396 
9397             if (DEBUG) {
9398                 StringBuilder b = new StringBuilder(128);
9399                 b.append("restoreSome token=");
9400                 b.append(Long.toHexString(token));
9401                 b.append(" observer=");
9402                 b.append(observer.toString());
9403                 b.append(" packages=");
9404                 if (packages == null) {
9405                     b.append("null");
9406                 } else {
9407                     b.append('{');
9408                     boolean first = true;
9409                     for (String s : packages) {
9410                         if (!first) {
9411                             b.append(", ");
9412                         } else first = false;
9413                         b.append(s);
9414                     }
9415                     b.append('}');
9416                 }
9417                 Slog.d(TAG, b.toString());
9418             }
9419 
9420             if (mEnded) {
9421                 throw new IllegalStateException("Restore session already ended");
9422             }
9423 
9424             if (mTimedOut) {
9425                 Slog.i(TAG, "Session already timed out");
9426                 return -1;
9427             }
9428 
9429             if (mRestoreTransport == null || mRestoreSets == null) {
9430                 Slog.e(TAG, "Ignoring restoreAll() with no restore set");
9431                 return -1;
9432             }
9433 
9434             if (mPackageName != null) {
9435                 Slog.e(TAG, "Ignoring restoreAll() on single-package session");
9436                 return -1;
9437             }
9438 
9439             String dirName;
9440             try {
9441                 dirName = mRestoreTransport.transportDirName();
9442             } catch (RemoteException e) {
9443                 // Transport went AWOL; fail.
9444                 Slog.e(TAG, "Unable to contact transport for restore");
9445                 return -1;
9446             }
9447 
9448             synchronized (mQueueLock) {
9449                 for (int i = 0; i < mRestoreSets.length; i++) {
9450                     if (token == mRestoreSets[i].token) {
9451                         // Stop the session timeout until we finalize the restore
9452                         mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9453 
9454                         long oldId = Binder.clearCallingIdentity();
9455                         mWakelock.acquire();
9456                         if (MORE_DEBUG) {
9457                             Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
9458                         }
9459                         Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9460                         msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
9461                                 packages, packages.length > 1);
9462                         mBackupHandler.sendMessage(msg);
9463                         Binder.restoreCallingIdentity(oldId);
9464                         return 0;
9465                     }
9466                 }
9467             }
9468 
9469             Slog.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
9470             return -1;
9471         }
9472 
restorePackage(String packageName, IRestoreObserver observer)9473         public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
9474             if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
9475 
9476             if (mEnded) {
9477                 throw new IllegalStateException("Restore session already ended");
9478             }
9479 
9480             if (mTimedOut) {
9481                 Slog.i(TAG, "Session already timed out");
9482                 return -1;
9483             }
9484 
9485             if (mPackageName != null) {
9486                 if (! mPackageName.equals(packageName)) {
9487                     Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
9488                             + " on session for package " + mPackageName);
9489                     return -1;
9490                 }
9491             }
9492 
9493             PackageInfo app = null;
9494             try {
9495                 app = mPackageManager.getPackageInfo(packageName, 0);
9496             } catch (NameNotFoundException nnf) {
9497                 Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
9498                 return -1;
9499             }
9500 
9501             // If the caller is not privileged and is not coming from the target
9502             // app's uid, throw a permission exception back to the caller.
9503             int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
9504                     Binder.getCallingPid(), Binder.getCallingUid());
9505             if ((perm == PackageManager.PERMISSION_DENIED) &&
9506                     (app.applicationInfo.uid != Binder.getCallingUid())) {
9507                 Slog.w(TAG, "restorePackage: bad packageName=" + packageName
9508                         + " or calling uid=" + Binder.getCallingUid());
9509                 throw new SecurityException("No permission to restore other packages");
9510             }
9511 
9512             // So far so good; we're allowed to try to restore this package.
9513             long oldId = Binder.clearCallingIdentity();
9514             try {
9515                 // Check whether there is data for it in the current dataset, falling back
9516                 // to the ancestral dataset if not.
9517                 long token = getAvailableRestoreToken(packageName);
9518                 if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName
9519                         + " token=" + Long.toHexString(token));
9520 
9521                 // If we didn't come up with a place to look -- no ancestral dataset and
9522                 // the app has never been backed up from this device -- there's nothing
9523                 // to do but return failure.
9524                 if (token == 0) {
9525                     if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
9526                     return -1;
9527                 }
9528 
9529                 String dirName;
9530                 try {
9531                     dirName = mRestoreTransport.transportDirName();
9532                 } catch (RemoteException e) {
9533                     // Transport went AWOL; fail.
9534                     Slog.e(TAG, "Unable to contact transport for restore");
9535                     return -1;
9536                 }
9537 
9538                 // Stop the session timeout until we finalize the restore
9539                 mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
9540 
9541                 // Ready to go:  enqueue the restore request and claim success
9542                 mWakelock.acquire();
9543                 if (MORE_DEBUG) {
9544                     Slog.d(TAG, "restorePackage() : " + packageName);
9545                 }
9546                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
9547                 msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app);
9548                 mBackupHandler.sendMessage(msg);
9549             } finally {
9550                 Binder.restoreCallingIdentity(oldId);
9551             }
9552             return 0;
9553         }
9554 
9555         // Posted to the handler to tear down a restore session in a cleanly synchronized way
9556         class EndRestoreRunnable implements Runnable {
9557             BackupManagerService mBackupManager;
9558             ActiveRestoreSession mSession;
9559 
EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session)9560             EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
9561                 mBackupManager = manager;
9562                 mSession = session;
9563             }
9564 
run()9565             public void run() {
9566                 // clean up the session's bookkeeping
9567                 synchronized (mSession) {
9568                     mSession.mRestoreTransport = null;
9569                     mSession.mEnded = true;
9570                 }
9571 
9572                 // clean up the BackupManagerImpl side of the bookkeeping
9573                 // and cancel any pending timeout message
9574                 mBackupManager.clearRestoreSession(mSession);
9575             }
9576         }
9577 
endRestoreSession()9578         public synchronized void endRestoreSession() {
9579             if (DEBUG) Slog.d(TAG, "endRestoreSession");
9580 
9581             if (mTimedOut) {
9582                 Slog.i(TAG, "Session already timed out");
9583                 return;
9584             }
9585 
9586             if (mEnded) {
9587                 throw new IllegalStateException("Restore session already ended");
9588             }
9589 
9590             mBackupHandler.post(new EndRestoreRunnable(BackupManagerService.this, this));
9591         }
9592     }
9593 
dump(FileDescriptor fd, PrintWriter pw, String[] args)9594     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
9595         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
9596 
9597         long identityToken = Binder.clearCallingIdentity();
9598         try {
9599             if (args != null) {
9600                 for (String arg : args) {
9601                     if ("-h".equals(arg)) {
9602                         pw.println("'dumpsys backup' optional arguments:");
9603                         pw.println("  -h       : this help text");
9604                         pw.println("  a[gents] : dump information about defined backup agents");
9605                         return;
9606                     } else if ("agents".startsWith(arg)) {
9607                         dumpAgents(pw);
9608                         return;
9609                     }
9610                 }
9611             }
9612             dumpInternal(pw);
9613         } finally {
9614             Binder.restoreCallingIdentity(identityToken);
9615         }
9616     }
9617 
dumpAgents(PrintWriter pw)9618     private void dumpAgents(PrintWriter pw) {
9619         List<PackageInfo> agentPackages = allAgentPackages();
9620         pw.println("Defined backup agents:");
9621         for (PackageInfo pkg : agentPackages) {
9622             pw.print("  ");
9623             pw.print(pkg.packageName); pw.println(':');
9624             pw.print("      "); pw.println(pkg.applicationInfo.backupAgentName);
9625         }
9626     }
9627 
dumpInternal(PrintWriter pw)9628     private void dumpInternal(PrintWriter pw) {
9629         synchronized (mQueueLock) {
9630             pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
9631                     + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
9632                     + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
9633             pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
9634             if (mBackupRunning) pw.println("Backup currently running");
9635             pw.println("Last backup pass started: " + mLastBackupPass
9636                     + " (now = " + System.currentTimeMillis() + ')');
9637             pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
9638 
9639             pw.println("Available transports:");
9640             final String[] transports = listAllTransports();
9641             if (transports != null) {
9642                 for (String t : listAllTransports()) {
9643                     pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
9644                     try {
9645                         IBackupTransport transport = getTransport(t);
9646                         File dir = new File(mBaseStateDir, transport.transportDirName());
9647                         pw.println("       destination: " + transport.currentDestinationString());
9648                         pw.println("       intent: " + transport.configurationIntent());
9649                         for (File f : dir.listFiles()) {
9650                             pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
9651                         }
9652                     } catch (Exception e) {
9653                         Slog.e(TAG, "Error in transport", e);
9654                         pw.println("        Error: " + e);
9655                     }
9656                 }
9657             }
9658 
9659             pw.println("Pending init: " + mPendingInits.size());
9660             for (String s : mPendingInits) {
9661                 pw.println("    " + s);
9662             }
9663 
9664             if (DEBUG_BACKUP_TRACE) {
9665                 synchronized (mBackupTrace) {
9666                     if (!mBackupTrace.isEmpty()) {
9667                         pw.println("Most recent backup trace:");
9668                         for (String s : mBackupTrace) {
9669                             pw.println("   " + s);
9670                         }
9671                     }
9672                 }
9673             }
9674 
9675             pw.print("Ancestral: "); pw.println(Long.toHexString(mAncestralToken));
9676             pw.print("Current:   "); pw.println(Long.toHexString(mCurrentToken));
9677 
9678             int N = mBackupParticipants.size();
9679             pw.println("Participants:");
9680             for (int i=0; i<N; i++) {
9681                 int uid = mBackupParticipants.keyAt(i);
9682                 pw.print("  uid: ");
9683                 pw.println(uid);
9684                 HashSet<String> participants = mBackupParticipants.valueAt(i);
9685                 for (String app: participants) {
9686                     pw.println("    " + app);
9687                 }
9688             }
9689 
9690             pw.println("Ancestral packages: "
9691                     + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
9692             if (mAncestralPackages != null) {
9693                 for (String pkg : mAncestralPackages) {
9694                     pw.println("    " + pkg);
9695                 }
9696             }
9697 
9698             pw.println("Ever backed up: " + mEverStoredApps.size());
9699             for (String pkg : mEverStoredApps) {
9700                 pw.println("    " + pkg);
9701             }
9702 
9703             pw.println("Pending key/value backup: " + mPendingBackups.size());
9704             for (BackupRequest req : mPendingBackups.values()) {
9705                 pw.println("    " + req);
9706             }
9707 
9708             pw.println("Full backup queue:" + mFullBackupQueue.size());
9709             for (FullBackupEntry entry : mFullBackupQueue) {
9710                 pw.print("    "); pw.print(entry.lastBackup);
9711                 pw.print(" : "); pw.println(entry.packageName);
9712             }
9713         }
9714     }
9715 }
9716