Home | History | Annotate | Download | only in backup
      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.PendingIntent;
     26 import android.app.backup.BackupAgent;
     27 import android.app.backup.BackupDataInput;
     28 import android.app.backup.BackupDataOutput;
     29 import android.app.backup.BackupTransport;
     30 import android.app.backup.FullBackup;
     31 import android.app.backup.RestoreDescription;
     32 import android.app.backup.RestoreSet;
     33 import android.app.backup.IBackupManager;
     34 import android.app.backup.IFullBackupRestoreObserver;
     35 import android.app.backup.IRestoreObserver;
     36 import android.app.backup.IRestoreSession;
     37 import android.content.ActivityNotFoundException;
     38 import android.content.BroadcastReceiver;
     39 import android.content.ComponentName;
     40 import android.content.ContentResolver;
     41 import android.content.Context;
     42 import android.content.Intent;
     43 import android.content.IntentFilter;
     44 import android.content.ServiceConnection;
     45 import android.content.pm.ApplicationInfo;
     46 import android.content.pm.IPackageDataObserver;
     47 import android.content.pm.IPackageDeleteObserver;
     48 import android.content.pm.IPackageInstallObserver;
     49 import android.content.pm.IPackageManager;
     50 import android.content.pm.PackageInfo;
     51 import android.content.pm.PackageManager;
     52 import android.content.pm.ResolveInfo;
     53 import android.content.pm.ServiceInfo;
     54 import android.content.pm.Signature;
     55 import android.content.pm.PackageManager.NameNotFoundException;
     56 import android.database.ContentObserver;
     57 import android.net.Uri;
     58 import android.os.Binder;
     59 import android.os.Build;
     60 import android.os.Bundle;
     61 import android.os.Environment;
     62 import android.os.Handler;
     63 import android.os.HandlerThread;
     64 import android.os.IBinder;
     65 import android.os.Looper;
     66 import android.os.Message;
     67 import android.os.ParcelFileDescriptor;
     68 import android.os.PowerManager;
     69 import android.os.Process;
     70 import android.os.RemoteException;
     71 import android.os.SELinux;
     72 import android.os.ServiceManager;
     73 import android.os.SystemClock;
     74 import android.os.UserHandle;
     75 import android.os.WorkSource;
     76 import android.os.Environment.UserEnvironment;
     77 import android.os.storage.IMountService;
     78 import android.os.storage.StorageManager;
     79 import android.provider.Settings;
     80 import android.system.ErrnoException;
     81 import android.system.Os;
     82 import android.util.ArrayMap;
     83 import android.util.AtomicFile;
     84 import android.util.EventLog;
     85 import android.util.Log;
     86 import android.util.Slog;
     87 import android.util.SparseArray;
     88 import android.util.StringBuilderPrinter;
     89 
     90 import com.android.internal.backup.IBackupTransport;
     91 import com.android.internal.backup.IObbBackupService;
     92 import com.android.server.AppWidgetBackupBridge;
     93 import com.android.server.EventLogTags;
     94 import com.android.server.SystemService;
     95 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
     96 
     97 import java.io.BufferedInputStream;
     98 import java.io.BufferedOutputStream;
     99 import java.io.ByteArrayInputStream;
    100 import java.io.ByteArrayOutputStream;
    101 import java.io.DataInputStream;
    102 import java.io.DataOutputStream;
    103 import java.io.EOFException;
    104 import java.io.File;
    105 import java.io.FileDescriptor;
    106 import java.io.FileInputStream;
    107 import java.io.FileNotFoundException;
    108 import java.io.FileOutputStream;
    109 import java.io.IOException;
    110 import java.io.InputStream;
    111 import java.io.OutputStream;
    112 import java.io.PrintWriter;
    113 import java.io.RandomAccessFile;
    114 import java.security.InvalidAlgorithmParameterException;
    115 import java.security.InvalidKeyException;
    116 import java.security.Key;
    117 import java.security.MessageDigest;
    118 import java.security.NoSuchAlgorithmException;
    119 import java.security.SecureRandom;
    120 import java.security.spec.InvalidKeySpecException;
    121 import java.security.spec.KeySpec;
    122 import java.text.SimpleDateFormat;
    123 import java.util.ArrayList;
    124 import java.util.Arrays;
    125 import java.util.Collections;
    126 import java.util.Date;
    127 import java.util.HashMap;
    128 import java.util.HashSet;
    129 import java.util.Iterator;
    130 import java.util.List;
    131 import java.util.Map;
    132 import java.util.Map.Entry;
    133 import java.util.Objects;
    134 import java.util.Random;
    135 import java.util.Set;
    136 import java.util.TreeMap;
    137 import java.util.concurrent.atomic.AtomicBoolean;
    138 import java.util.concurrent.atomic.AtomicInteger;
    139 import java.util.zip.Deflater;
    140 import java.util.zip.DeflaterOutputStream;
    141 import java.util.zip.InflaterInputStream;
    142 
    143 import javax.crypto.BadPaddingException;
    144 import javax.crypto.Cipher;
    145 import javax.crypto.CipherInputStream;
    146 import javax.crypto.CipherOutputStream;
    147 import javax.crypto.IllegalBlockSizeException;
    148 import javax.crypto.NoSuchPaddingException;
    149 import javax.crypto.SecretKey;
    150 import javax.crypto.SecretKeyFactory;
    151 import javax.crypto.spec.IvParameterSpec;
    152 import javax.crypto.spec.PBEKeySpec;
    153 import javax.crypto.spec.SecretKeySpec;
    154 
    155 import libcore.io.IoUtils;
    156 
    157 public class BackupManagerService {
    158 
    159     private static final String TAG = "BackupManagerService";
    160     private static final boolean DEBUG = true;
    161     private static final boolean MORE_DEBUG = false;
    162     private static final boolean DEBUG_SCHEDULING = MORE_DEBUG || true;
    163 
    164     // System-private key used for backing up an app's widget state.  Must
    165     // begin with U+FFxx by convention (we reserve all keys starting
    166     // with U+FF00 or higher for system use).
    167     static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
    168 
    169     // Historical and current algorithm names
    170     static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
    171     static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
    172 
    173     // Name and current contents version of the full-backup manifest file
    174     //
    175     // Manifest version history:
    176     //
    177     // 1 : initial release
    178     static final String BACKUP_MANIFEST_FILENAME = "_manifest";
    179     static final int BACKUP_MANIFEST_VERSION = 1;
    180 
    181     // External archive format version history:
    182     //
    183     // 1 : initial release
    184     // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
    185     // 3 : introduced "_meta" metadata file; no other format change per se
    186     static final int BACKUP_FILE_VERSION = 3;
    187     static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
    188     static final int BACKUP_PW_FILE_VERSION = 2;
    189     static final String BACKUP_METADATA_FILENAME = "_meta";
    190     static final int BACKUP_METADATA_VERSION = 1;
    191     static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
    192     static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
    193 
    194     static final String SETTINGS_PACKAGE = "com.android.providers.settings";
    195     static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
    196     static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
    197 
    198     // How often we perform a backup pass.  Privileged external callers can
    199     // trigger an immediate pass.
    200     private static final long BACKUP_INTERVAL = AlarmManager.INTERVAL_HOUR;
    201 
    202     // Random variation in backup scheduling time to avoid server load spikes
    203     private static final int FUZZ_MILLIS = 5 * 60 * 1000;
    204 
    205     // The amount of time between the initial provisioning of the device and
    206     // the first backup pass.
    207     private static final long FIRST_BACKUP_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR;
    208 
    209     // Retry interval for clear/init when the transport is unavailable
    210     private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
    211 
    212     private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
    213     private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
    214     private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
    215     private static final int MSG_RUN_BACKUP = 1;
    216     private static final int MSG_RUN_ADB_BACKUP = 2;
    217     private static final int MSG_RUN_RESTORE = 3;
    218     private static final int MSG_RUN_CLEAR = 4;
    219     private static final int MSG_RUN_INITIALIZE = 5;
    220     private static final int MSG_RUN_GET_RESTORE_SETS = 6;
    221     private static final int MSG_TIMEOUT = 7;
    222     private static final int MSG_RESTORE_TIMEOUT = 8;
    223     private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
    224     private static final int MSG_RUN_ADB_RESTORE = 10;
    225     private static final int MSG_RETRY_INIT = 11;
    226     private static final int MSG_RETRY_CLEAR = 12;
    227     private static final int MSG_WIDGET_BROADCAST = 13;
    228     private static final int MSG_RUN_FULL_TRANSPORT_BACKUP = 14;
    229 
    230     // backup task state machine tick
    231     static final int MSG_BACKUP_RESTORE_STEP = 20;
    232     static final int MSG_OP_COMPLETE = 21;
    233 
    234     // Timeout interval for deciding that a bind or clear-data has taken too long
    235     static final long TIMEOUT_INTERVAL = 10 * 1000;
    236 
    237     // Timeout intervals for agent backup & restore operations
    238     static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
    239     static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
    240     static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
    241     static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
    242     static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
    243 
    244     // User confirmation timeout for a full backup/restore operation.  It's this long in
    245     // order to give them time to enter the backup password.
    246     static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
    247 
    248     // How long between attempts to perform a full-data backup of any given app
    249     static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
    250 
    251     Context mContext;
    252     private PackageManager mPackageManager;
    253     IPackageManager mPackageManagerBinder;
    254     private IActivityManager mActivityManager;
    255     private PowerManager mPowerManager;
    256     private AlarmManager mAlarmManager;
    257     private IMountService mMountService;
    258     IBackupManager mBackupManagerBinder;
    259 
    260     boolean mEnabled;   // access to this is synchronized on 'this'
    261     boolean mProvisioned;
    262     boolean mAutoRestore;
    263     PowerManager.WakeLock mWakelock;
    264     HandlerThread mHandlerThread;
    265     BackupHandler mBackupHandler;
    266     PendingIntent mRunBackupIntent, mRunInitIntent;
    267     BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
    268     // map UIDs to the set of participating packages under that UID
    269     final SparseArray<HashSet<String>> mBackupParticipants
    270             = new SparseArray<HashSet<String>>();
    271     // set of backup services that have pending changes
    272     class BackupRequest {
    273         public String packageName;
    274 
    275         BackupRequest(String pkgName) {
    276             packageName = pkgName;
    277         }
    278 
    279         public String toString() {
    280             return "BackupRequest{pkg=" + packageName + "}";
    281         }
    282     }
    283     // Backups that we haven't started yet.  Keys are package names.
    284     HashMap<String,BackupRequest> mPendingBackups
    285             = new HashMap<String,BackupRequest>();
    286 
    287     // Pseudoname that we use for the Package Manager metadata "package"
    288     static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
    289 
    290     // locking around the pending-backup management
    291     final Object mQueueLock = new Object();
    292 
    293     // The thread performing the sequence of queued backups binds to each app's agent
    294     // in succession.  Bind notifications are asynchronously delivered through the
    295     // Activity Manager; use this lock object to signal when a requested binding has
    296     // completed.
    297     final Object mAgentConnectLock = new Object();
    298     IBackupAgent mConnectedAgent;
    299     volatile boolean mBackupRunning;
    300     volatile boolean mConnecting;
    301     volatile long mLastBackupPass;
    302     volatile long mNextBackupPass;
    303 
    304     // For debugging, we maintain a progress trace of operations during backup
    305     static final boolean DEBUG_BACKUP_TRACE = true;
    306     final List<String> mBackupTrace = new ArrayList<String>();
    307 
    308     // A similar synchronization mechanism around clearing apps' data for restore
    309     final Object mClearDataLock = new Object();
    310     volatile boolean mClearingData;
    311 
    312     // Transport bookkeeping
    313     final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
    314     final ArrayMap<String,String> mTransportNames
    315             = new ArrayMap<String,String>();             // component name -> registration name
    316     final ArrayMap<String,IBackupTransport> mTransports
    317             = new ArrayMap<String,IBackupTransport>();   // registration name -> binder
    318     final ArrayMap<String,TransportConnection> mTransportConnections
    319             = new ArrayMap<String,TransportConnection>();
    320     String mCurrentTransport;
    321     ActiveRestoreSession mActiveRestoreSession;
    322 
    323     // Watch the device provisioning operation during setup
    324     ContentObserver mProvisionedObserver;
    325 
    326     // The published binder is actually to a singleton trampoline object that calls
    327     // through to the proper code.  This indirection lets us turn down the heavy
    328     // implementation object on the fly without disturbing binders that have been
    329     // cached elsewhere in the system.
    330     static Trampoline sInstance;
    331     static Trampoline getInstance() {
    332         // Always constructed during system bringup, so no need to lazy-init
    333         return sInstance;
    334     }
    335 
    336     public static final class Lifecycle extends SystemService {
    337 
    338         public Lifecycle(Context context) {
    339             super(context);
    340             sInstance = new Trampoline(context);
    341         }
    342 
    343         @Override
    344         public void onStart() {
    345             publishBinderService(Context.BACKUP_SERVICE, sInstance);
    346         }
    347 
    348         @Override
    349         public void onBootPhase(int phase) {
    350             if (phase == PHASE_SYSTEM_SERVICES_READY) {
    351                 sInstance.initialize(UserHandle.USER_OWNER);
    352             } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
    353                 ContentResolver r = sInstance.mContext.getContentResolver();
    354                 boolean areEnabled = Settings.Secure.getInt(r,
    355                         Settings.Secure.BACKUP_ENABLED, 0) != 0;
    356                 try {
    357                     sInstance.setBackupEnabled(areEnabled);
    358                 } catch (RemoteException e) {
    359                     // can't happen; it's a local object
    360                 }
    361             }
    362         }
    363     }
    364 
    365     class ProvisionedObserver extends ContentObserver {
    366         public ProvisionedObserver(Handler handler) {
    367             super(handler);
    368         }
    369 
    370         public void onChange(boolean selfChange) {
    371             final boolean wasProvisioned = mProvisioned;
    372             final boolean isProvisioned = deviceIsProvisioned();
    373             // latch: never unprovision
    374             mProvisioned = wasProvisioned || isProvisioned;
    375             if (MORE_DEBUG) {
    376                 Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
    377                         + " is=" + isProvisioned + " now=" + mProvisioned);
    378             }
    379 
    380             synchronized (mQueueLock) {
    381                 if (mProvisioned && !wasProvisioned && mEnabled) {
    382                     // we're now good to go, so start the backup alarms
    383                     if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
    384                     startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
    385                     scheduleNextFullBackupJob();
    386                 }
    387             }
    388         }
    389     }
    390 
    391     class RestoreGetSetsParams {
    392         public IBackupTransport transport;
    393         public ActiveRestoreSession session;
    394         public IRestoreObserver observer;
    395 
    396         RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
    397                 IRestoreObserver _observer) {
    398             transport = _transport;
    399             session = _session;
    400             observer = _observer;
    401         }
    402     }
    403 
    404     class RestoreParams {
    405         public IBackupTransport transport;
    406         public String dirName;
    407         public IRestoreObserver observer;
    408         public long token;
    409         public PackageInfo pkgInfo;
    410         public int pmToken; // in post-install restore, the PM's token for this transaction
    411         public boolean isSystemRestore;
    412         public String[] filterSet;
    413 
    414         // Restore a single package
    415         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
    416                 long _token, PackageInfo _pkg, int _pmToken) {
    417             transport = _transport;
    418             dirName = _dirName;
    419             observer = _obs;
    420             token = _token;
    421             pkgInfo = _pkg;
    422             pmToken = _pmToken;
    423             isSystemRestore = false;
    424             filterSet = null;
    425         }
    426 
    427         // Restore everything possible.  This is the form that Setup Wizard or similar
    428         // restore UXes use.
    429         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
    430                 long _token) {
    431             transport = _transport;
    432             dirName = _dirName;
    433             observer = _obs;
    434             token = _token;
    435             pkgInfo = null;
    436             pmToken = 0;
    437             isSystemRestore = true;
    438             filterSet = null;
    439         }
    440 
    441         // Restore some set of packages.  Leave this one up to the caller to specify
    442         // whether it's to be considered a system-level restore.
    443         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
    444                 long _token, String[] _filterSet, boolean _isSystemRestore) {
    445             transport = _transport;
    446             dirName = _dirName;
    447             observer = _obs;
    448             token = _token;
    449             pkgInfo = null;
    450             pmToken = 0;
    451             isSystemRestore = _isSystemRestore;
    452             filterSet = _filterSet;
    453         }
    454     }
    455 
    456     class ClearParams {
    457         public IBackupTransport transport;
    458         public PackageInfo packageInfo;
    459 
    460         ClearParams(IBackupTransport _transport, PackageInfo _info) {
    461             transport = _transport;
    462             packageInfo = _info;
    463         }
    464     }
    465 
    466     class ClearRetryParams {
    467         public String transportName;
    468         public String packageName;
    469 
    470         ClearRetryParams(String transport, String pkg) {
    471             transportName = transport;
    472             packageName = pkg;
    473         }
    474     }
    475 
    476     class FullParams {
    477         public ParcelFileDescriptor fd;
    478         public final AtomicBoolean latch;
    479         public IFullBackupRestoreObserver observer;
    480         public String curPassword;     // filled in by the confirmation step
    481         public String encryptPassword;
    482 
    483         FullParams() {
    484             latch = new AtomicBoolean(false);
    485         }
    486     }
    487 
    488     class FullBackupParams extends FullParams {
    489         public boolean includeApks;
    490         public boolean includeObbs;
    491         public boolean includeShared;
    492         public boolean doWidgets;
    493         public boolean allApps;
    494         public boolean includeSystem;
    495         public boolean doCompress;
    496         public String[] packages;
    497 
    498         FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
    499                 boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
    500                 boolean compress, String[] pkgList) {
    501             fd = output;
    502             includeApks = saveApks;
    503             includeObbs = saveObbs;
    504             includeShared = saveShared;
    505             doWidgets = alsoWidgets;
    506             allApps = doAllApps;
    507             includeSystem = doSystem;
    508             doCompress = compress;
    509             packages = pkgList;
    510         }
    511     }
    512 
    513     class FullRestoreParams extends FullParams {
    514         FullRestoreParams(ParcelFileDescriptor input) {
    515             fd = input;
    516         }
    517     }
    518 
    519     // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
    520     // token is the index of the entry in the pending-operations list.
    521     static final int OP_PENDING = 0;
    522     static final int OP_ACKNOWLEDGED = 1;
    523     static final int OP_TIMEOUT = -1;
    524 
    525     class Operation {
    526         public int state;
    527         public BackupRestoreTask callback;
    528 
    529         Operation(int initialState, BackupRestoreTask callbackObj) {
    530             state = initialState;
    531             callback = callbackObj;
    532         }
    533     }
    534     final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
    535     final Object mCurrentOpLock = new Object();
    536     final Random mTokenGenerator = new Random();
    537 
    538     final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
    539 
    540     // Where we keep our journal files and other bookkeeping
    541     File mBaseStateDir;
    542     File mDataDir;
    543     File mJournalDir;
    544     File mJournal;
    545 
    546     // Backup password, if any, and the file where it's saved.  What is stored is not the
    547     // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but
    548     // persisted) salt.  Validation is performed by running the challenge text through the
    549     // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches
    550     // the saved hash string, then the challenge text matches the originally supplied
    551     // password text.
    552     private final SecureRandom mRng = new SecureRandom();
    553     private String mPasswordHash;
    554     private File mPasswordHashFile;
    555     private int mPasswordVersion;
    556     private File mPasswordVersionFile;
    557     private byte[] mPasswordSalt;
    558 
    559     // Configuration of PBKDF2 that we use for generating pw hashes and intermediate keys
    560     static final int PBKDF2_HASH_ROUNDS = 10000;
    561     static final int PBKDF2_KEY_SIZE = 256;     // bits
    562     static final int PBKDF2_SALT_SIZE = 512;    // bits
    563     static final String ENCRYPTION_ALGORITHM_NAME = "AES-256";
    564 
    565     // Keep a log of all the apps we've ever backed up, and what the
    566     // dataset tokens are for both the current backup dataset and
    567     // the ancestral dataset.
    568     private File mEverStored;
    569     HashSet<String> mEverStoredApps = new HashSet<String>();
    570 
    571     static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;  // increment when the schema changes
    572     File mTokenFile;
    573     Set<String> mAncestralPackages = null;
    574     long mAncestralToken = 0;
    575     long mCurrentToken = 0;
    576 
    577     // Persistently track the need to do a full init
    578     static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
    579     HashSet<String> mPendingInits = new HashSet<String>();  // transport names
    580 
    581     // Round-robin queue for scheduling full backup passes
    582     static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file
    583     class FullBackupEntry implements Comparable<FullBackupEntry> {
    584         String packageName;
    585         long lastBackup;
    586 
    587         FullBackupEntry(String pkg, long when) {
    588             packageName = pkg;
    589             lastBackup = when;
    590         }
    591 
    592         @Override
    593         public int compareTo(FullBackupEntry other) {
    594             if (lastBackup < other.lastBackup) return -1;
    595             else if (lastBackup > other.lastBackup) return 1;
    596             else return 0;
    597         }
    598     }
    599 
    600     File mFullBackupScheduleFile;
    601     // If we're running a schedule-driven full backup, this is the task instance doing it
    602     PerformFullTransportBackupTask mRunningFullBackupTask; // inside mQueueLock
    603     ArrayList<FullBackupEntry> mFullBackupQueue;           // inside mQueueLock
    604 
    605     // Utility: build a new random integer token
    606     int generateToken() {
    607         int token;
    608         do {
    609             synchronized (mTokenGenerator) {
    610                 token = mTokenGenerator.nextInt();
    611             }
    612         } while (token < 0);
    613         return token;
    614     }
    615 
    616     // High level policy: apps are ineligible for backup if certain conditions apply
    617     public static boolean appIsEligibleForBackup(ApplicationInfo app) {
    618         // 1. their manifest states android:allowBackup="false"
    619         if ((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
    620             return false;
    621         }
    622 
    623         // 2. they run as a system-level uid but do not supply their own backup agent
    624         if ((app.uid < Process.FIRST_APPLICATION_UID) && (app.backupAgentName == null)) {
    625             return false;
    626         }
    627 
    628         // 3. it is the special shared-storage backup package used for 'adb backup'
    629         if (app.packageName.equals(BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE)) {
    630             return false;
    631         }
    632 
    633         return true;
    634     }
    635 
    636     /* does *not* check overall backup eligibility policy! */
    637     public static boolean appGetsFullBackup(PackageInfo pkg) {
    638         if (pkg.applicationInfo.backupAgentName != null) {
    639             // If it has an agent, it gets full backups only if it says so
    640             return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0;
    641         }
    642 
    643         // No agent means we do full backups for it
    644         return true;
    645     }
    646 
    647     // ----- Asynchronous backup/restore handler thread -----
    648 
    649     private class BackupHandler extends Handler {
    650         public BackupHandler(Looper looper) {
    651             super(looper);
    652         }
    653 
    654         public void handleMessage(Message msg) {
    655 
    656             switch (msg.what) {
    657             case MSG_RUN_BACKUP:
    658             {
    659                 mLastBackupPass = System.currentTimeMillis();
    660                 mNextBackupPass = mLastBackupPass + BACKUP_INTERVAL;
    661 
    662                 IBackupTransport transport = getTransport(mCurrentTransport);
    663                 if (transport == null) {
    664                     Slog.v(TAG, "Backup requested but no transport available");
    665                     synchronized (mQueueLock) {
    666                         mBackupRunning = false;
    667                     }
    668                     mWakelock.release();
    669                     break;
    670                 }
    671 
    672                 // snapshot the pending-backup set and work on that
    673                 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
    674                 File oldJournal = mJournal;
    675                 synchronized (mQueueLock) {
    676                     // Do we have any work to do?  Construct the work queue
    677                     // then release the synchronization lock to actually run
    678                     // the backup.
    679                     if (mPendingBackups.size() > 0) {
    680                         for (BackupRequest b: mPendingBackups.values()) {
    681                             queue.add(b);
    682                         }
    683                         if (DEBUG) Slog.v(TAG, "clearing pending backups");
    684                         mPendingBackups.clear();
    685 
    686                         // Start a new backup-queue journal file too
    687                         mJournal = null;
    688 
    689                     }
    690                 }
    691 
    692                 // At this point, we have started a new journal file, and the old
    693                 // file identity is being passed to the backup processing task.
    694                 // When it completes successfully, that old journal file will be
    695                 // deleted.  If we crash prior to that, the old journal is parsed
    696                 // at next boot and the journaled requests fulfilled.
    697                 boolean staged = true;
    698                 if (queue.size() > 0) {
    699                     // Spin up a backup state sequence and set it running
    700                     try {
    701                         String dirName = transport.transportDirName();
    702                         PerformBackupTask pbt = new PerformBackupTask(transport, dirName,
    703                                 queue, oldJournal);
    704                         Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
    705                         sendMessage(pbtMessage);
    706                     } catch (RemoteException e) {
    707                         // unable to ask the transport its dir name -- transient failure, since
    708                         // the above check succeeded.  Try again next time.
    709                         Slog.e(TAG, "Transport became unavailable attempting backup");
    710                         staged = false;
    711                     }
    712                 } else {
    713                     Slog.v(TAG, "Backup requested but nothing pending");
    714                     staged = false;
    715                 }
    716 
    717                 if (!staged) {
    718                     // if we didn't actually hand off the wakelock, rewind until next time
    719                     synchronized (mQueueLock) {
    720                         mBackupRunning = false;
    721                     }
    722                     mWakelock.release();
    723                 }
    724                 break;
    725             }
    726 
    727             case MSG_BACKUP_RESTORE_STEP:
    728             {
    729                 try {
    730                     BackupRestoreTask task = (BackupRestoreTask) msg.obj;
    731                     if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing");
    732                     task.execute();
    733                 } catch (ClassCastException e) {
    734                     Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
    735                 }
    736                 break;
    737             }
    738 
    739             case MSG_OP_COMPLETE:
    740             {
    741                 try {
    742                     BackupRestoreTask task = (BackupRestoreTask) msg.obj;
    743                     task.operationComplete();
    744                 } catch (ClassCastException e) {
    745                     Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
    746                 }
    747                 break;
    748             }
    749 
    750             case MSG_RUN_ADB_BACKUP:
    751             {
    752                 // TODO: refactor full backup to be a looper-based state machine
    753                 // similar to normal backup/restore.
    754                 FullBackupParams params = (FullBackupParams)msg.obj;
    755                 PerformAdbBackupTask task = new PerformAdbBackupTask(params.fd,
    756                         params.observer, params.includeApks, params.includeObbs,
    757                         params.includeShared, params.doWidgets,
    758                         params.curPassword, params.encryptPassword,
    759                         params.allApps, params.includeSystem, params.doCompress,
    760                         params.packages, params.latch);
    761                 (new Thread(task, "adb-backup")).start();
    762                 break;
    763             }
    764 
    765             case MSG_RUN_FULL_TRANSPORT_BACKUP:
    766             {
    767                 PerformFullTransportBackupTask task = (PerformFullTransportBackupTask) msg.obj;
    768                 (new Thread(task, "transport-backup")).start();
    769                 break;
    770             }
    771 
    772             case MSG_RUN_RESTORE:
    773             {
    774                 RestoreParams params = (RestoreParams)msg.obj;
    775                 Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
    776                 BackupRestoreTask task = new PerformUnifiedRestoreTask(params.transport,
    777                         params.observer, params.token, params.pkgInfo, params.pmToken,
    778                         params.isSystemRestore, params.filterSet);
    779                 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
    780                 sendMessage(restoreMsg);
    781                 break;
    782             }
    783 
    784             case MSG_RUN_ADB_RESTORE:
    785             {
    786                 // TODO: refactor full restore to be a looper-based state machine
    787                 // similar to normal backup/restore.
    788                 FullRestoreParams params = (FullRestoreParams)msg.obj;
    789                 PerformAdbRestoreTask task = new PerformAdbRestoreTask(params.fd,
    790                         params.curPassword, params.encryptPassword,
    791                         params.observer, params.latch);
    792                 (new Thread(task, "adb-restore")).start();
    793                 break;
    794             }
    795 
    796             case MSG_RUN_CLEAR:
    797             {
    798                 ClearParams params = (ClearParams)msg.obj;
    799                 (new PerformClearTask(params.transport, params.packageInfo)).run();
    800                 break;
    801             }
    802 
    803             case MSG_RETRY_CLEAR:
    804             {
    805                 // reenqueues if the transport remains unavailable
    806                 ClearRetryParams params = (ClearRetryParams)msg.obj;
    807                 clearBackupData(params.transportName, params.packageName);
    808                 break;
    809             }
    810 
    811             case MSG_RUN_INITIALIZE:
    812             {
    813                 HashSet<String> queue;
    814 
    815                 // Snapshot the pending-init queue and work on that
    816                 synchronized (mQueueLock) {
    817                     queue = new HashSet<String>(mPendingInits);
    818                     mPendingInits.clear();
    819                 }
    820 
    821                 (new PerformInitializeTask(queue)).run();
    822                 break;
    823             }
    824 
    825             case MSG_RETRY_INIT:
    826             {
    827                 synchronized (mQueueLock) {
    828                     recordInitPendingLocked(msg.arg1 != 0, (String)msg.obj);
    829                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
    830                             mRunInitIntent);
    831                 }
    832                 break;
    833             }
    834 
    835             case MSG_RUN_GET_RESTORE_SETS:
    836             {
    837                 // Like other async operations, this is entered with the wakelock held
    838                 RestoreSet[] sets = null;
    839                 RestoreGetSetsParams params = (RestoreGetSetsParams)msg.obj;
    840                 try {
    841                     sets = params.transport.getAvailableRestoreSets();
    842                     // cache the result in the active session
    843                     synchronized (params.session) {
    844                         params.session.mRestoreSets = sets;
    845                     }
    846                     if (sets == null) EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
    847                 } catch (Exception e) {
    848                     Slog.e(TAG, "Error from transport getting set list");
    849                 } finally {
    850                     if (params.observer != null) {
    851                         try {
    852                             params.observer.restoreSetsAvailable(sets);
    853                         } catch (RemoteException re) {
    854                             Slog.e(TAG, "Unable to report listing to observer");
    855                         } catch (Exception e) {
    856                             Slog.e(TAG, "Restore observer threw", e);
    857                         }
    858                     }
    859 
    860                     // Done: reset the session timeout clock
    861                     removeMessages(MSG_RESTORE_TIMEOUT);
    862                     sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
    863 
    864                     mWakelock.release();
    865                 }
    866                 break;
    867             }
    868 
    869             case MSG_TIMEOUT:
    870             {
    871                 handleTimeout(msg.arg1, msg.obj);
    872                 break;
    873             }
    874 
    875             case MSG_RESTORE_TIMEOUT:
    876             {
    877                 synchronized (BackupManagerService.this) {
    878                     if (mActiveRestoreSession != null) {
    879                         // Client app left the restore session dangling.  We know that it
    880                         // can't be in the middle of an actual restore operation because
    881                         // the timeout is suspended while a restore is in progress.  Clean
    882                         // up now.
    883                         Slog.w(TAG, "Restore session timed out; aborting");
    884                         mActiveRestoreSession.markTimedOut();
    885                         post(mActiveRestoreSession.new EndRestoreRunnable(
    886                                 BackupManagerService.this, mActiveRestoreSession));
    887                     }
    888                 }
    889                 break;
    890             }
    891 
    892             case MSG_FULL_CONFIRMATION_TIMEOUT:
    893             {
    894                 synchronized (mFullConfirmations) {
    895                     FullParams params = mFullConfirmations.get(msg.arg1);
    896                     if (params != null) {
    897                         Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
    898 
    899                         // Release the waiter; timeout == completion
    900                         signalFullBackupRestoreCompletion(params);
    901 
    902                         // Remove the token from the set
    903                         mFullConfirmations.delete(msg.arg1);
    904 
    905                         // Report a timeout to the observer, if any
    906                         if (params.observer != null) {
    907                             try {
    908                                 params.observer.onTimeout();
    909                             } catch (RemoteException e) {
    910                                 /* don't care if the app has gone away */
    911                             }
    912                         }
    913                     } else {
    914                         Slog.d(TAG, "couldn't find params for token " + msg.arg1);
    915                     }
    916                 }
    917                 break;
    918             }
    919 
    920             case MSG_WIDGET_BROADCAST:
    921             {
    922                 final Intent intent = (Intent) msg.obj;
    923                 mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
    924                 break;
    925             }
    926             }
    927         }
    928     }
    929 
    930     // ----- Debug-only backup operation trace -----
    931     void addBackupTrace(String s) {
    932         if (DEBUG_BACKUP_TRACE) {
    933             synchronized (mBackupTrace) {
    934                 mBackupTrace.add(s);
    935             }
    936         }
    937     }
    938 
    939     void clearBackupTrace() {
    940         if (DEBUG_BACKUP_TRACE) {
    941             synchronized (mBackupTrace) {
    942                 mBackupTrace.clear();
    943             }
    944         }
    945     }
    946 
    947     // ----- Main service implementation -----
    948 
    949     public BackupManagerService(Context context, Trampoline parent) {
    950         mContext = context;
    951         mPackageManager = context.getPackageManager();
    952         mPackageManagerBinder = AppGlobals.getPackageManager();
    953         mActivityManager = ActivityManagerNative.getDefault();
    954 
    955         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    956         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    957         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
    958 
    959         mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
    960 
    961         // spin up the backup/restore handler thread
    962         mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
    963         mHandlerThread.start();
    964         mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
    965 
    966         // Set up our bookkeeping
    967         final ContentResolver resolver = context.getContentResolver();
    968         mProvisioned = Settings.Global.getInt(resolver,
    969                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
    970         mAutoRestore = Settings.Secure.getInt(resolver,
    971                 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
    972 
    973         mProvisionedObserver = new ProvisionedObserver(mBackupHandler);
    974         resolver.registerContentObserver(
    975                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
    976                 false, mProvisionedObserver);
    977 
    978         // If Encrypted file systems is enabled or disabled, this call will return the
    979         // correct directory.
    980         mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
    981         mBaseStateDir.mkdirs();
    982         if (!SELinux.restorecon(mBaseStateDir)) {
    983             Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
    984         }
    985         mDataDir = Environment.getDownloadCacheDirectory();
    986 
    987         mPasswordVersion = 1;       // unless we hear otherwise
    988         mPasswordVersionFile = new File(mBaseStateDir, "pwversion");
    989         if (mPasswordVersionFile.exists()) {
    990             FileInputStream fin = null;
    991             DataInputStream in = null;
    992             try {
    993                 fin = new FileInputStream(mPasswordVersionFile);
    994                 in = new DataInputStream(fin);
    995                 mPasswordVersion = in.readInt();
    996             } catch (IOException e) {
    997                 Slog.e(TAG, "Unable to read backup pw version");
    998             } finally {
    999                 try {
   1000                     if (in != null) in.close();
   1001                     if (fin != null) fin.close();
   1002                 } catch (IOException e) {
   1003                     Slog.w(TAG, "Error closing pw version files");
   1004                 }
   1005             }
   1006         }
   1007 
   1008         mPasswordHashFile = new File(mBaseStateDir, "pwhash");
   1009         if (mPasswordHashFile.exists()) {
   1010             FileInputStream fin = null;
   1011             DataInputStream in = null;
   1012             try {
   1013                 fin = new FileInputStream(mPasswordHashFile);
   1014                 in = new DataInputStream(new BufferedInputStream(fin));
   1015                 // integer length of the salt array, followed by the salt,
   1016                 // then the hex pw hash string
   1017                 int saltLen = in.readInt();
   1018                 byte[] salt = new byte[saltLen];
   1019                 in.readFully(salt);
   1020                 mPasswordHash = in.readUTF();
   1021                 mPasswordSalt = salt;
   1022             } catch (IOException e) {
   1023                 Slog.e(TAG, "Unable to read saved backup pw hash");
   1024             } finally {
   1025                 try {
   1026                     if (in != null) in.close();
   1027                     if (fin != null) fin.close();
   1028                 } catch (IOException e) {
   1029                     Slog.w(TAG, "Unable to close streams");
   1030                 }
   1031             }
   1032         }
   1033 
   1034         // Alarm receivers for scheduled backups & initialization operations
   1035         mRunBackupReceiver = new RunBackupReceiver();
   1036         IntentFilter filter = new IntentFilter();
   1037         filter.addAction(RUN_BACKUP_ACTION);
   1038         context.registerReceiver(mRunBackupReceiver, filter,
   1039                 android.Manifest.permission.BACKUP, null);
   1040 
   1041         mRunInitReceiver = new RunInitializeReceiver();
   1042         filter = new IntentFilter();
   1043         filter.addAction(RUN_INITIALIZE_ACTION);
   1044         context.registerReceiver(mRunInitReceiver, filter,
   1045                 android.Manifest.permission.BACKUP, null);
   1046 
   1047         Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
   1048         backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
   1049         mRunBackupIntent = PendingIntent.getBroadcast(context, MSG_RUN_BACKUP, backupIntent, 0);
   1050 
   1051         Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
   1052         backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
   1053         mRunInitIntent = PendingIntent.getBroadcast(context, MSG_RUN_INITIALIZE, initIntent, 0);
   1054 
   1055         // Set up the backup-request journaling
   1056         mJournalDir = new File(mBaseStateDir, "pending");
   1057         mJournalDir.mkdirs();   // creates mBaseStateDir along the way
   1058         mJournal = null;        // will be created on first use
   1059 
   1060         // Set up the various sorts of package tracking we do
   1061         mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
   1062         initPackageTracking();
   1063 
   1064         // Build our mapping of uid to backup client services.  This implicitly
   1065         // schedules a backup pass on the Package Manager metadata the first
   1066         // time anything needs to be backed up.
   1067         synchronized (mBackupParticipants) {
   1068             addPackageParticipantsLocked(null);
   1069         }
   1070 
   1071         // Set up our transport options and initialize the default transport
   1072         // TODO: Don't create transports that we don't need to?
   1073         mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
   1074                 Settings.Secure.BACKUP_TRANSPORT);
   1075         if ("".equals(mCurrentTransport)) {
   1076             mCurrentTransport = null;
   1077         }
   1078         if (DEBUG) Slog.v(TAG, "Starting with transport " + mCurrentTransport);
   1079 
   1080         // Find all transport hosts and bind to their services
   1081         List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
   1082                 mTransportServiceIntent, 0, UserHandle.USER_OWNER);
   1083         if (DEBUG) {
   1084             Slog.v(TAG, "Found transports: " + ((hosts == null) ? "null" : hosts.size()));
   1085         }
   1086         if (hosts != null) {
   1087             for (int i = 0; i < hosts.size(); i++) {
   1088                 final ServiceInfo transport = hosts.get(i).serviceInfo;
   1089                 if (MORE_DEBUG) {
   1090                     Slog.v(TAG, "   " + transport.packageName + "/" + transport.name);
   1091                 }
   1092                 tryBindTransport(transport);
   1093             }
   1094         }
   1095 
   1096         // Now that we know about valid backup participants, parse any
   1097         // leftover journal files into the pending backup set
   1098         parseLeftoverJournals();
   1099 
   1100         // Power management
   1101         mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
   1102     }
   1103 
   1104     private class RunBackupReceiver extends BroadcastReceiver {
   1105         public void onReceive(Context context, Intent intent) {
   1106             if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
   1107                 synchronized (mQueueLock) {
   1108                     if (mPendingInits.size() > 0) {
   1109                         // If there are pending init operations, we process those
   1110                         // and then settle into the usual periodic backup schedule.
   1111                         if (DEBUG) Slog.v(TAG, "Init pending at scheduled backup");
   1112                         try {
   1113                             mAlarmManager.cancel(mRunInitIntent);
   1114                             mRunInitIntent.send();
   1115                         } catch (PendingIntent.CanceledException ce) {
   1116                             Slog.e(TAG, "Run init intent cancelled");
   1117                             // can't really do more than bail here
   1118                         }
   1119                     } else {
   1120                         // Don't run backups now if we're disabled or not yet
   1121                         // fully set up.
   1122                         if (mEnabled && mProvisioned) {
   1123                             if (!mBackupRunning) {
   1124                                 if (DEBUG) Slog.v(TAG, "Running a backup pass");
   1125 
   1126                                 // Acquire the wakelock and pass it to the backup thread.  it will
   1127                                 // be released once backup concludes.
   1128                                 mBackupRunning = true;
   1129                                 mWakelock.acquire();
   1130 
   1131                                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
   1132                                 mBackupHandler.sendMessage(msg);
   1133                             } else {
   1134                                 Slog.i(TAG, "Backup time but one already running");
   1135                             }
   1136                         } else {
   1137                             Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
   1138                         }
   1139                     }
   1140                 }
   1141             }
   1142         }
   1143     }
   1144 
   1145     private class RunInitializeReceiver extends BroadcastReceiver {
   1146         public void onReceive(Context context, Intent intent) {
   1147             if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
   1148                 synchronized (mQueueLock) {
   1149                     if (DEBUG) Slog.v(TAG, "Running a device init");
   1150 
   1151                     // Acquire the wakelock and pass it to the init thread.  it will
   1152                     // be released once init concludes.
   1153                     mWakelock.acquire();
   1154 
   1155                     Message msg = mBackupHandler.obtainMessage(MSG_RUN_INITIALIZE);
   1156                     mBackupHandler.sendMessage(msg);
   1157                 }
   1158             }
   1159         }
   1160     }
   1161 
   1162     private void initPackageTracking() {
   1163         if (MORE_DEBUG) Slog.v(TAG, "` tracking");
   1164 
   1165         // Remember our ancestral dataset
   1166         mTokenFile = new File(mBaseStateDir, "ancestral");
   1167         try {
   1168             RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
   1169             int version = tf.readInt();
   1170             if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
   1171                 mAncestralToken = tf.readLong();
   1172                 mCurrentToken = tf.readLong();
   1173 
   1174                 int numPackages = tf.readInt();
   1175                 if (numPackages >= 0) {
   1176                     mAncestralPackages = new HashSet<String>();
   1177                     for (int i = 0; i < numPackages; i++) {
   1178                         String pkgName = tf.readUTF();
   1179                         mAncestralPackages.add(pkgName);
   1180                     }
   1181                 }
   1182             }
   1183             tf.close();
   1184         } catch (FileNotFoundException fnf) {
   1185             // Probably innocuous
   1186             Slog.v(TAG, "No ancestral data");
   1187         } catch (IOException e) {
   1188             Slog.w(TAG, "Unable to read token file", e);
   1189         }
   1190 
   1191         // Keep a log of what apps we've ever backed up.  Because we might have
   1192         // rebooted in the middle of an operation that was removing something from
   1193         // this log, we sanity-check its contents here and reconstruct it.
   1194         mEverStored = new File(mBaseStateDir, "processed");
   1195         File tempProcessedFile = new File(mBaseStateDir, "processed.new");
   1196 
   1197         // If we were in the middle of removing something from the ever-backed-up
   1198         // file, there might be a transient "processed.new" file still present.
   1199         // Ignore it -- we'll validate "processed" against the current package set.
   1200         if (tempProcessedFile.exists()) {
   1201             tempProcessedFile.delete();
   1202         }
   1203 
   1204         // If there are previous contents, parse them out then start a new
   1205         // file to continue the recordkeeping.
   1206         if (mEverStored.exists()) {
   1207             RandomAccessFile temp = null;
   1208             RandomAccessFile in = null;
   1209 
   1210             try {
   1211                 temp = new RandomAccessFile(tempProcessedFile, "rws");
   1212                 in = new RandomAccessFile(mEverStored, "r");
   1213 
   1214                 while (true) {
   1215                     PackageInfo info;
   1216                     String pkg = in.readUTF();
   1217                     try {
   1218                         info = mPackageManager.getPackageInfo(pkg, 0);
   1219                         mEverStoredApps.add(pkg);
   1220                         temp.writeUTF(pkg);
   1221                         if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
   1222                     } catch (NameNotFoundException e) {
   1223                         // nope, this package was uninstalled; don't include it
   1224                         if (MORE_DEBUG) Slog.v(TAG, "   - " + pkg);
   1225                     }
   1226                 }
   1227             } catch (EOFException e) {
   1228                 // Once we've rewritten the backup history log, atomically replace the
   1229                 // old one with the new one then reopen the file for continuing use.
   1230                 if (!tempProcessedFile.renameTo(mEverStored)) {
   1231                     Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
   1232                 }
   1233             } catch (IOException e) {
   1234                 Slog.e(TAG, "Error in processed file", e);
   1235             } finally {
   1236                 try { if (temp != null) temp.close(); } catch (IOException e) {}
   1237                 try { if (in != null) in.close(); } catch (IOException e) {}
   1238             }
   1239         }
   1240 
   1241         // Resume the full-data backup queue
   1242         mFullBackupQueue = readFullBackupSchedule();
   1243 
   1244         // Register for broadcasts about package install, etc., so we can
   1245         // update the provider list.
   1246         IntentFilter filter = new IntentFilter();
   1247         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
   1248         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
   1249         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
   1250         filter.addDataScheme("package");
   1251         mContext.registerReceiver(mBroadcastReceiver, filter);
   1252         // Register for events related to sdcard installation.
   1253         IntentFilter sdFilter = new IntentFilter();
   1254         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
   1255         sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
   1256         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
   1257     }
   1258 
   1259     private ArrayList<FullBackupEntry> readFullBackupSchedule() {
   1260         ArrayList<FullBackupEntry> schedule = null;
   1261         synchronized (mQueueLock) {
   1262             if (mFullBackupScheduleFile.exists()) {
   1263                 FileInputStream fstream = null;
   1264                 BufferedInputStream bufStream = null;
   1265                 DataInputStream in = null;
   1266                 try {
   1267                     fstream = new FileInputStream(mFullBackupScheduleFile);
   1268                     bufStream = new BufferedInputStream(fstream);
   1269                     in = new DataInputStream(bufStream);
   1270 
   1271                     int version = in.readInt();
   1272                     if (version != SCHEDULE_FILE_VERSION) {
   1273                         Slog.e(TAG, "Unknown backup schedule version " + version);
   1274                         return null;
   1275                     }
   1276 
   1277                     int N = in.readInt();
   1278                     schedule = new ArrayList<FullBackupEntry>(N);
   1279                     for (int i = 0; i < N; i++) {
   1280                         String pkgName = in.readUTF();
   1281                         long lastBackup = in.readLong();
   1282                         schedule.add(new FullBackupEntry(pkgName, lastBackup));
   1283                     }
   1284                     Collections.sort(schedule);
   1285                 } catch (Exception e) {
   1286                     Slog.e(TAG, "Unable to read backup schedule", e);
   1287                     mFullBackupScheduleFile.delete();
   1288                     schedule = null;
   1289                 } finally {
   1290                     IoUtils.closeQuietly(in);
   1291                     IoUtils.closeQuietly(bufStream);
   1292                     IoUtils.closeQuietly(fstream);
   1293                 }
   1294             }
   1295 
   1296             if (schedule == null) {
   1297                 // no prior queue record, or unable to read it.  Set up the queue
   1298                 // from scratch.
   1299                 List<PackageInfo> apps =
   1300                         PackageManagerBackupAgent.getStorableApplications(mPackageManager);
   1301                 final int N = apps.size();
   1302                 schedule = new ArrayList<FullBackupEntry>(N);
   1303                 for (int i = 0; i < N; i++) {
   1304                     PackageInfo info = apps.get(i);
   1305                     if (appGetsFullBackup(info)) {
   1306                         schedule.add(new FullBackupEntry(info.packageName, 0));
   1307                     }
   1308                 }
   1309                 writeFullBackupScheduleAsync();
   1310             }
   1311         }
   1312         return schedule;
   1313     }
   1314 
   1315     Runnable mFullBackupScheduleWriter = new Runnable() {
   1316         @Override public void run() {
   1317             synchronized (mQueueLock) {
   1318                 try {
   1319                     ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
   1320                     DataOutputStream bufOut = new DataOutputStream(bufStream);
   1321                     bufOut.writeInt(SCHEDULE_FILE_VERSION);
   1322 
   1323                     // version 1:
   1324                     //
   1325                     // [int] # of packages in the queue = N
   1326                     // N * {
   1327                     //     [utf8] package name
   1328                     //     [long] last backup time for this package
   1329                     //     }
   1330                     int N = mFullBackupQueue.size();
   1331                     bufOut.writeInt(N);
   1332 
   1333                     for (int i = 0; i < N; i++) {
   1334                         FullBackupEntry entry = mFullBackupQueue.get(i);
   1335                         bufOut.writeUTF(entry.packageName);
   1336                         bufOut.writeLong(entry.lastBackup);
   1337                     }
   1338                     bufOut.flush();
   1339 
   1340                     AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
   1341                     FileOutputStream out = af.startWrite();
   1342                     out.write(bufStream.toByteArray());
   1343                     af.finishWrite(out);
   1344                 } catch (Exception e) {
   1345                     Slog.e(TAG, "Unable to write backup schedule!", e);
   1346                 }
   1347             }
   1348         }
   1349     };
   1350 
   1351     private void writeFullBackupScheduleAsync() {
   1352         mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
   1353         mBackupHandler.post(mFullBackupScheduleWriter);
   1354     }
   1355 
   1356     private void parseLeftoverJournals() {
   1357         for (File f : mJournalDir.listFiles()) {
   1358             if (mJournal == null || f.compareTo(mJournal) != 0) {
   1359                 // This isn't the current journal, so it must be a leftover.  Read
   1360                 // out the package names mentioned there and schedule them for
   1361                 // backup.
   1362                 RandomAccessFile in = null;
   1363                 try {
   1364                     Slog.i(TAG, "Found stale backup journal, scheduling");
   1365                     in = new RandomAccessFile(f, "r");
   1366                     while (true) {
   1367                         String packageName = in.readUTF();
   1368                         if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
   1369                         dataChangedImpl(packageName);
   1370                     }
   1371                 } catch (EOFException e) {
   1372                     // no more data; we're done
   1373                 } catch (Exception e) {
   1374                     Slog.e(TAG, "Can't read " + f, e);
   1375                 } finally {
   1376                     // close/delete the file
   1377                     try { if (in != null) in.close(); } catch (IOException e) {}
   1378                     f.delete();
   1379                 }
   1380             }
   1381         }
   1382     }
   1383 
   1384     private SecretKey buildPasswordKey(String algorithm, String pw, byte[] salt, int rounds) {
   1385         return buildCharArrayKey(algorithm, pw.toCharArray(), salt, rounds);
   1386     }
   1387 
   1388     private SecretKey buildCharArrayKey(String algorithm, char[] pwArray, byte[] salt, int rounds) {
   1389         try {
   1390             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
   1391             KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
   1392             return keyFactory.generateSecret(ks);
   1393         } catch (InvalidKeySpecException e) {
   1394             Slog.e(TAG, "Invalid key spec for PBKDF2!");
   1395         } catch (NoSuchAlgorithmException e) {
   1396             Slog.e(TAG, "PBKDF2 unavailable!");
   1397         }
   1398         return null;
   1399     }
   1400 
   1401     private String buildPasswordHash(String algorithm, String pw, byte[] salt, int rounds) {
   1402         SecretKey key = buildPasswordKey(algorithm, pw, salt, rounds);
   1403         if (key != null) {
   1404             return byteArrayToHex(key.getEncoded());
   1405         }
   1406         return null;
   1407     }
   1408 
   1409     private String byteArrayToHex(byte[] data) {
   1410         StringBuilder buf = new StringBuilder(data.length * 2);
   1411         for (int i = 0; i < data.length; i++) {
   1412             buf.append(Byte.toHexString(data[i], true));
   1413         }
   1414         return buf.toString();
   1415     }
   1416 
   1417     private byte[] hexToByteArray(String digits) {
   1418         final int bytes = digits.length() / 2;
   1419         if (2*bytes != digits.length()) {
   1420             throw new IllegalArgumentException("Hex string must have an even number of digits");
   1421         }
   1422 
   1423         byte[] result = new byte[bytes];
   1424         for (int i = 0; i < digits.length(); i += 2) {
   1425             result[i/2] = (byte) Integer.parseInt(digits.substring(i, i+2), 16);
   1426         }
   1427         return result;
   1428     }
   1429 
   1430     private byte[] makeKeyChecksum(String algorithm, byte[] pwBytes, byte[] salt, int rounds) {
   1431         char[] mkAsChar = new char[pwBytes.length];
   1432         for (int i = 0; i < pwBytes.length; i++) {
   1433             mkAsChar[i] = (char) pwBytes[i];
   1434         }
   1435 
   1436         Key checksum = buildCharArrayKey(algorithm, mkAsChar, salt, rounds);
   1437         return checksum.getEncoded();
   1438     }
   1439 
   1440     // Used for generating random salts or passwords
   1441     private byte[] randomBytes(int bits) {
   1442         byte[] array = new byte[bits / 8];
   1443         mRng.nextBytes(array);
   1444         return array;
   1445     }
   1446 
   1447     boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) {
   1448         if (mPasswordHash == null) {
   1449             // no current password case -- require that 'currentPw' be null or empty
   1450             if (candidatePw == null || "".equals(candidatePw)) {
   1451                 return true;
   1452             } // else the non-empty candidate does not match the empty stored pw
   1453         } else {
   1454             // hash the stated current pw and compare to the stored one
   1455             if (candidatePw != null && candidatePw.length() > 0) {
   1456                 String currentPwHash = buildPasswordHash(algorithm, candidatePw, mPasswordSalt, rounds);
   1457                 if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
   1458                     // candidate hash matches the stored hash -- the password matches
   1459                     return true;
   1460                 }
   1461             } // else the stored pw is nonempty but the candidate is empty; no match
   1462         }
   1463         return false;
   1464     }
   1465 
   1466     public boolean setBackupPassword(String currentPw, String newPw) {
   1467         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
   1468                 "setBackupPassword");
   1469 
   1470         // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes
   1471         final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
   1472 
   1473         // If the supplied pw doesn't hash to the the saved one, fail.  The password
   1474         // might be caught in the legacy crypto mismatch; verify that too.
   1475         if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
   1476                 && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
   1477                         currentPw, PBKDF2_HASH_ROUNDS))) {
   1478             return false;
   1479         }
   1480 
   1481         // Snap up to current on the pw file version
   1482         mPasswordVersion = BACKUP_PW_FILE_VERSION;
   1483         FileOutputStream pwFout = null;
   1484         DataOutputStream pwOut = null;
   1485         try {
   1486             pwFout = new FileOutputStream(mPasswordVersionFile);
   1487             pwOut = new DataOutputStream(pwFout);
   1488             pwOut.writeInt(mPasswordVersion);
   1489         } catch (IOException e) {
   1490             Slog.e(TAG, "Unable to write backup pw version; password not changed");
   1491             return false;
   1492         } finally {
   1493             try {
   1494                 if (pwOut != null) pwOut.close();
   1495                 if (pwFout != null) pwFout.close();
   1496             } catch (IOException e) {
   1497                 Slog.w(TAG, "Unable to close pw version record");
   1498             }
   1499         }
   1500 
   1501         // Clearing the password is okay
   1502         if (newPw == null || newPw.isEmpty()) {
   1503             if (mPasswordHashFile.exists()) {
   1504                 if (!mPasswordHashFile.delete()) {
   1505                     // Unable to delete the old pw file, so fail
   1506                     Slog.e(TAG, "Unable to clear backup password");
   1507                     return false;
   1508                 }
   1509             }
   1510             mPasswordHash = null;
   1511             mPasswordSalt = null;
   1512             return true;
   1513         }
   1514 
   1515         try {
   1516             // Okay, build the hash of the new backup password
   1517             byte[] salt = randomBytes(PBKDF2_SALT_SIZE);
   1518             String newPwHash = buildPasswordHash(PBKDF_CURRENT, newPw, salt, PBKDF2_HASH_ROUNDS);
   1519 
   1520             OutputStream pwf = null, buffer = null;
   1521             DataOutputStream out = null;
   1522             try {
   1523                 pwf = new FileOutputStream(mPasswordHashFile);
   1524                 buffer = new BufferedOutputStream(pwf);
   1525                 out = new DataOutputStream(buffer);
   1526                 // integer length of the salt array, followed by the salt,
   1527                 // then the hex pw hash string
   1528                 out.writeInt(salt.length);
   1529                 out.write(salt);
   1530                 out.writeUTF(newPwHash);
   1531                 out.flush();
   1532                 mPasswordHash = newPwHash;
   1533                 mPasswordSalt = salt;
   1534                 return true;
   1535             } finally {
   1536                 if (out != null) out.close();
   1537                 if (buffer != null) buffer.close();
   1538                 if (pwf != null) pwf.close();
   1539             }
   1540         } catch (IOException e) {
   1541             Slog.e(TAG, "Unable to set backup password");
   1542         }
   1543         return false;
   1544     }
   1545 
   1546     public boolean hasBackupPassword() {
   1547         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
   1548                 "hasBackupPassword");
   1549 
   1550         return mPasswordHash != null && mPasswordHash.length() > 0;
   1551     }
   1552 
   1553     private boolean backupPasswordMatches(String currentPw) {
   1554         if (hasBackupPassword()) {
   1555             final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
   1556             if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PBKDF2_HASH_ROUNDS)
   1557                     && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
   1558                             currentPw, PBKDF2_HASH_ROUNDS))) {
   1559                 if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
   1560                 return false;
   1561             }
   1562         }
   1563         return true;
   1564     }
   1565 
   1566     // Maintain persistent state around whether need to do an initialize operation.
   1567     // Must be called with the queue lock held.
   1568     void recordInitPendingLocked(boolean isPending, String transportName) {
   1569         if (DEBUG) Slog.i(TAG, "recordInitPendingLocked: " + isPending
   1570                 + " on transport " + transportName);
   1571         mBackupHandler.removeMessages(MSG_RETRY_INIT);
   1572 
   1573         try {
   1574             IBackupTransport transport = getTransport(transportName);
   1575             if (transport != null) {
   1576                 String transportDirName = transport.transportDirName();
   1577                 File stateDir = new File(mBaseStateDir, transportDirName);
   1578                 File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
   1579 
   1580                 if (isPending) {
   1581                     // We need an init before we can proceed with sending backup data.
   1582                     // Record that with an entry in our set of pending inits, as well as
   1583                     // journaling it via creation of a sentinel file.
   1584                     mPendingInits.add(transportName);
   1585                     try {
   1586                         (new FileOutputStream(initPendingFile)).close();
   1587                     } catch (IOException ioe) {
   1588                         // Something is badly wrong with our permissions; just try to move on
   1589                     }
   1590                 } else {
   1591                     // No more initialization needed; wipe the journal and reset our state.
   1592                     initPendingFile.delete();
   1593                     mPendingInits.remove(transportName);
   1594                 }
   1595                 return; // done; don't fall through to the error case
   1596             }
   1597         } catch (RemoteException e) {
   1598             // transport threw when asked its name; fall through to the lookup-failed case
   1599         }
   1600 
   1601         // The named transport doesn't exist or threw.  This operation is
   1602         // important, so we record the need for a an init and post a message
   1603         // to retry the init later.
   1604         if (isPending) {
   1605             mPendingInits.add(transportName);
   1606             mBackupHandler.sendMessageDelayed(
   1607                     mBackupHandler.obtainMessage(MSG_RETRY_INIT,
   1608                             (isPending ? 1 : 0),
   1609                             0,
   1610                             transportName),
   1611                     TRANSPORT_RETRY_INTERVAL);
   1612         }
   1613     }
   1614 
   1615     // Reset all of our bookkeeping, in response to having been told that
   1616     // the backend data has been wiped [due to idle expiry, for example],
   1617     // so we must re-upload all saved settings.
   1618     void resetBackupState(File stateFileDir) {
   1619         synchronized (mQueueLock) {
   1620             // Wipe the "what we've ever backed up" tracking
   1621             mEverStoredApps.clear();
   1622             mEverStored.delete();
   1623 
   1624             mCurrentToken = 0;
   1625             writeRestoreTokens();
   1626 
   1627             // Remove all the state files
   1628             for (File sf : stateFileDir.listFiles()) {
   1629                 // ... but don't touch the needs-init sentinel
   1630                 if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
   1631                     sf.delete();
   1632                 }
   1633             }
   1634         }
   1635 
   1636         // Enqueue a new backup of every participant
   1637         synchronized (mBackupParticipants) {
   1638             final int N = mBackupParticipants.size();
   1639             for (int i=0; i<N; i++) {
   1640                 HashSet<String> participants = mBackupParticipants.valueAt(i);
   1641                 if (participants != null) {
   1642                     for (String packageName : participants) {
   1643                         dataChangedImpl(packageName);
   1644                     }
   1645                 }
   1646             }
   1647         }
   1648     }
   1649 
   1650     // Add a transport to our set of available backends.  If 'transport' is null, this
   1651     // is an unregistration, and the transport's entry is removed from our bookkeeping.
   1652     private void registerTransport(String name, String component, IBackupTransport transport) {
   1653         synchronized (mTransports) {
   1654             if (DEBUG) Slog.v(TAG, "Registering transport "
   1655                     + component + "::" + name + " = " + transport);
   1656             if (transport != null) {
   1657                 mTransports.put(name, transport);
   1658                 mTransportNames.put(component, name);
   1659             } else {
   1660                 mTransports.remove(mTransportNames.get(component));
   1661                 mTransportNames.remove(component);
   1662                 // Nothing further to do in the unregistration case
   1663                 return;
   1664             }
   1665         }
   1666 
   1667         // If the init sentinel file exists, we need to be sure to perform the init
   1668         // as soon as practical.  We also create the state directory at registration
   1669         // time to ensure it's present from the outset.
   1670         try {
   1671             String transportName = transport.transportDirName();
   1672             File stateDir = new File(mBaseStateDir, transportName);
   1673             stateDir.mkdirs();
   1674 
   1675             File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
   1676             if (initSentinel.exists()) {
   1677                 synchronized (mQueueLock) {
   1678                     mPendingInits.add(transportName);
   1679 
   1680                     // TODO: pick a better starting time than now + 1 minute
   1681                     long delay = 1000 * 60; // one minute, in milliseconds
   1682                     mAlarmManager.set(AlarmManager.RTC_WAKEUP,
   1683                             System.currentTimeMillis() + delay, mRunInitIntent);
   1684                 }
   1685             }
   1686         } catch (RemoteException e) {
   1687             // the transport threw when asked its file naming prefs; declare it invalid
   1688             Slog.e(TAG, "Unable to register transport as " + name);
   1689             mTransportNames.remove(component);
   1690             mTransports.remove(name);
   1691         }
   1692     }
   1693 
   1694     // ----- Track installation/removal of packages -----
   1695     BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
   1696         public void onReceive(Context context, Intent intent) {
   1697             if (DEBUG) Slog.d(TAG, "Received broadcast " + intent);
   1698 
   1699             String action = intent.getAction();
   1700             boolean replacing = false;
   1701             boolean added = false;
   1702             boolean changed = false;
   1703             Bundle extras = intent.getExtras();
   1704             String pkgList[] = null;
   1705             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
   1706                     Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
   1707                     Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
   1708                 Uri uri = intent.getData();
   1709                 if (uri == null) {
   1710                     return;
   1711                 }
   1712                 String pkgName = uri.getSchemeSpecificPart();
   1713                 if (pkgName != null) {
   1714                     pkgList = new String[] { pkgName };
   1715                 }
   1716                 changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
   1717 
   1718                 // At package-changed we only care about looking at new transport states
   1719                 if (changed) {
   1720                     try {
   1721                         if (MORE_DEBUG) {
   1722                             Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
   1723                         }
   1724                         // unbind existing possibly-stale connections to that package's transports
   1725                         synchronized (mTransports) {
   1726                             TransportConnection conn = mTransportConnections.get(pkgName);
   1727                             if (conn != null) {
   1728                                 final ServiceInfo svc = conn.mTransport;
   1729                                 ComponentName svcName =
   1730                                         new ComponentName(svc.packageName, svc.name);
   1731                                 String flatName = svcName.flattenToShortString();
   1732                                 Slog.i(TAG, "Unbinding " + svcName);
   1733 
   1734                                 mContext.unbindService(conn);
   1735                                 mTransportConnections.remove(pkgName);
   1736                                 mTransports.remove(mTransportNames.get(flatName));
   1737                                 mTransportNames.remove(flatName);
   1738                             }
   1739                         }
   1740                         // and then (re)bind as appropriate
   1741                         PackageInfo app = mPackageManager.getPackageInfo(pkgName, 0);
   1742                         checkForTransportAndBind(app);
   1743                     } catch (NameNotFoundException e) {
   1744                         // Nope, can't find it - just ignore
   1745                         if (MORE_DEBUG) {
   1746                             Slog.w(TAG, "Can't find changed package " + pkgName);
   1747                         }
   1748                     }
   1749                     return; // nothing more to do in the PACKAGE_CHANGED case
   1750                 }
   1751 
   1752                 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
   1753                 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
   1754             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
   1755                 added = true;
   1756                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
   1757             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
   1758                 added = false;
   1759                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
   1760             }
   1761 
   1762             if (pkgList == null || pkgList.length == 0) {
   1763                 return;
   1764             }
   1765 
   1766             final int uid = extras.getInt(Intent.EXTRA_UID);
   1767             if (added) {
   1768                 synchronized (mBackupParticipants) {
   1769                     if (replacing) {
   1770                         // This is the package-replaced case; we just remove the entry
   1771                         // under the old uid and fall through to re-add.
   1772                         removePackageParticipantsLocked(pkgList, uid);
   1773                     }
   1774                     addPackageParticipantsLocked(pkgList);
   1775                 }
   1776                 // If they're full-backup candidates, add them there instead
   1777                 for (String packageName : pkgList) {
   1778                     try {
   1779                         PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
   1780                         long now = System.currentTimeMillis();
   1781                         if (appGetsFullBackup(app)) {
   1782                             enqueueFullBackup(packageName, now);
   1783                             scheduleNextFullBackupJob();
   1784                         }
   1785 
   1786                         // Transport maintenance: rebind to known existing transports that have
   1787                         // just been updated; and bind to any newly-installed transport services.
   1788                         synchronized (mTransports) {
   1789                             final TransportConnection conn = mTransportConnections.get(packageName);
   1790                             if (conn != null) {
   1791                                 if (MORE_DEBUG) {
   1792                                     Slog.i(TAG, "Transport package changed; rebinding");
   1793                                 }
   1794                                 bindTransport(conn.mTransport);
   1795                             } else {
   1796                                 checkForTransportAndBind(app);
   1797                             }
   1798                         }
   1799 
   1800                     } catch (NameNotFoundException e) {
   1801                         // doesn't really exist; ignore it
   1802                         if (DEBUG) {
   1803                             Slog.i(TAG, "Can't resolve new app " + packageName);
   1804                         }
   1805                     }
   1806                 }
   1807 
   1808             } else {
   1809                 if (replacing) {
   1810                     // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
   1811                 } else {
   1812                     synchronized (mBackupParticipants) {
   1813                         removePackageParticipantsLocked(pkgList, uid);
   1814                     }
   1815                 }
   1816             }
   1817         }
   1818     };
   1819 
   1820     // ----- Track connection to transports service -----
   1821     class TransportConnection implements ServiceConnection {
   1822         ServiceInfo mTransport;
   1823 
   1824         public TransportConnection(ServiceInfo transport) {
   1825             mTransport = transport;
   1826         }
   1827 
   1828         @Override
   1829         public void onServiceConnected(ComponentName component, IBinder service) {
   1830             if (DEBUG) Slog.v(TAG, "Connected to transport " + component);
   1831             final String name = component.flattenToShortString();
   1832             try {
   1833                 IBackupTransport transport = IBackupTransport.Stub.asInterface(service);
   1834                 registerTransport(transport.name(), name, transport);
   1835                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 1);
   1836             } catch (RemoteException e) {
   1837                 Slog.e(TAG, "Unable to register transport " + component);
   1838                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
   1839             }
   1840         }
   1841 
   1842         @Override
   1843         public void onServiceDisconnected(ComponentName component) {
   1844             if (DEBUG) Slog.v(TAG, "Disconnected from transport " + component);
   1845             final String name = component.flattenToShortString();
   1846             EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, name, 0);
   1847             registerTransport(null, name, null);
   1848         }
   1849     };
   1850 
   1851     // Check whether the given package hosts a transport, and bind if so
   1852     void checkForTransportAndBind(PackageInfo pkgInfo) {
   1853         Intent intent = new Intent(mTransportServiceIntent)
   1854                 .setPackage(pkgInfo.packageName);
   1855         List<ResolveInfo> hosts = mPackageManager.queryIntentServicesAsUser(
   1856                 intent, 0, UserHandle.USER_OWNER);
   1857         final int N = hosts.size();
   1858         for (int i = 0; i < N; i++) {
   1859             final ServiceInfo info = hosts.get(i).serviceInfo;
   1860             tryBindTransport(info);
   1861         }
   1862     }
   1863 
   1864     // Verify that the service exists and is hosted by a privileged app, then proceed to bind
   1865     boolean tryBindTransport(ServiceInfo info) {
   1866         try {
   1867             PackageInfo packInfo = mPackageManager.getPackageInfo(info.packageName, 0);
   1868             if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
   1869                 return bindTransport(info);
   1870             } else {
   1871                 Slog.w(TAG, "Transport package " + info.packageName + " not privileged");
   1872             }
   1873         } catch (NameNotFoundException e) {
   1874             Slog.w(TAG, "Problem resolving transport package " + info.packageName);
   1875         }
   1876         return false;
   1877     }
   1878 
   1879     // Actually bind; presumes that we have already validated the transport service
   1880     boolean bindTransport(ServiceInfo transport) {
   1881         ComponentName svcName = new ComponentName(transport.packageName, transport.name);
   1882         if (DEBUG) {
   1883             Slog.i(TAG, "Binding to transport host " + svcName);
   1884         }
   1885         Intent intent = new Intent(mTransportServiceIntent);
   1886         intent.setComponent(svcName);
   1887 
   1888         TransportConnection connection;
   1889         synchronized (mTransports) {
   1890             connection = mTransportConnections.get(transport.packageName);
   1891             if (null == connection) {
   1892                 connection = new TransportConnection(transport);
   1893                 mTransportConnections.put(transport.packageName, connection);
   1894             } else {
   1895                 // This is a rebind due to package upgrade.  The service won't be
   1896                 // automatically relaunched for us until we explicitly rebind, but
   1897                 // we need to unbind the now-orphaned original connection.
   1898                 mContext.unbindService(connection);
   1899             }
   1900         }
   1901         return mContext.bindServiceAsUser(intent,
   1902                 connection, Context.BIND_AUTO_CREATE,
   1903                 UserHandle.OWNER);
   1904     }
   1905 
   1906     // Add the backup agents in the given packages to our set of known backup participants.
   1907     // If 'packageNames' is null, adds all backup agents in the whole system.
   1908     void addPackageParticipantsLocked(String[] packageNames) {
   1909         // Look for apps that define the android:backupAgent attribute
   1910         List<PackageInfo> targetApps = allAgentPackages();
   1911         if (packageNames != null) {
   1912             if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
   1913             for (String packageName : packageNames) {
   1914                 addPackageParticipantsLockedInner(packageName, targetApps);
   1915             }
   1916         } else {
   1917             if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
   1918             addPackageParticipantsLockedInner(null, targetApps);
   1919         }
   1920     }
   1921 
   1922     private void addPackageParticipantsLockedInner(String packageName,
   1923             List<PackageInfo> targetPkgs) {
   1924         if (MORE_DEBUG) {
   1925             Slog.v(TAG, "Examining " + packageName + " for backup agent");
   1926         }
   1927 
   1928         for (PackageInfo pkg : targetPkgs) {
   1929             if (packageName == null || pkg.packageName.equals(packageName)) {
   1930                 int uid = pkg.applicationInfo.uid;
   1931                 HashSet<String> set = mBackupParticipants.get(uid);
   1932                 if (set == null) {
   1933                     set = new HashSet<String>();
   1934                     mBackupParticipants.put(uid, set);
   1935                 }
   1936                 set.add(pkg.packageName);
   1937                 if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
   1938 
   1939                 // Schedule a backup for it on general principles
   1940                 if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
   1941                 dataChangedImpl(pkg.packageName);
   1942             }
   1943         }
   1944     }
   1945 
   1946     // Remove the given packages' entries from our known active set.
   1947     void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
   1948         if (packageNames == null) {
   1949             Slog.w(TAG, "removePackageParticipants with null list");
   1950             return;
   1951         }
   1952 
   1953         if (MORE_DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
   1954                 + " #" + packageNames.length);
   1955         for (String pkg : packageNames) {
   1956             // Known previous UID, so we know which package set to check
   1957             HashSet<String> set = mBackupParticipants.get(oldUid);
   1958             if (set != null && set.contains(pkg)) {
   1959                 removePackageFromSetLocked(set, pkg);
   1960                 if (set.isEmpty()) {
   1961                     if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
   1962                     mBackupParticipants.remove(oldUid);
   1963                 }
   1964             }
   1965         }
   1966     }
   1967 
   1968     private void removePackageFromSetLocked(final HashSet<String> set,
   1969             final String packageName) {
   1970         if (set.contains(packageName)) {
   1971             // Found it.  Remove this one package from the bookkeeping, and
   1972             // if it's the last participating app under this uid we drop the
   1973             // (now-empty) set as well.
   1974             // Note that we deliberately leave it 'known' in the "ever backed up"
   1975             // bookkeeping so that its current-dataset data will be retrieved
   1976             // if the app is subsequently reinstalled
   1977             if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
   1978             set.remove(packageName);
   1979             mPendingBackups.remove(packageName);
   1980         }
   1981     }
   1982 
   1983     // Returns the set of all applications that define an android:backupAgent attribute
   1984     List<PackageInfo> allAgentPackages() {
   1985         // !!! TODO: cache this and regenerate only when necessary
   1986         int flags = PackageManager.GET_SIGNATURES;
   1987         List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
   1988         int N = packages.size();
   1989         for (int a = N-1; a >= 0; a--) {
   1990             PackageInfo pkg = packages.get(a);
   1991             try {
   1992                 ApplicationInfo app = pkg.applicationInfo;
   1993                 if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
   1994                         || app.backupAgentName == null) {
   1995                     packages.remove(a);
   1996                 }
   1997                 else {
   1998                     // we will need the shared library path, so look that up and store it here.
   1999                     // This is used implicitly when we pass the PackageInfo object off to
   2000                     // the Activity Manager to launch the app for backup/restore purposes.
   2001                     app = mPackageManager.getApplicationInfo(pkg.packageName,
   2002                             PackageManager.GET_SHARED_LIBRARY_FILES);
   2003                     pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
   2004                 }
   2005             } catch (NameNotFoundException e) {
   2006                 packages.remove(a);
   2007             }
   2008         }
   2009         return packages;
   2010     }
   2011 
   2012     // Called from the backup tasks: record that the given app has been successfully
   2013     // backed up at least once.  This includes both key/value and full-data backups
   2014     // through the transport.
   2015     void logBackupComplete(String packageName) {
   2016         if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
   2017 
   2018         synchronized (mEverStoredApps) {
   2019             if (!mEverStoredApps.add(packageName)) return;
   2020 
   2021             RandomAccessFile out = null;
   2022             try {
   2023                 out = new RandomAccessFile(mEverStored, "rws");
   2024                 out.seek(out.length());
   2025                 out.writeUTF(packageName);
   2026             } catch (IOException e) {
   2027                 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
   2028             } finally {
   2029                 try { if (out != null) out.close(); } catch (IOException e) {}
   2030             }
   2031         }
   2032     }
   2033 
   2034     // Remove our awareness of having ever backed up the given package
   2035     void removeEverBackedUp(String packageName) {
   2036         if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
   2037         if (MORE_DEBUG) Slog.v(TAG, "New set:");
   2038 
   2039         synchronized (mEverStoredApps) {
   2040             // Rewrite the file and rename to overwrite.  If we reboot in the middle,
   2041             // we'll recognize on initialization time that the package no longer
   2042             // exists and fix it up then.
   2043             File tempKnownFile = new File(mBaseStateDir, "processed.new");
   2044             RandomAccessFile known = null;
   2045             try {
   2046                 known = new RandomAccessFile(tempKnownFile, "rws");
   2047                 mEverStoredApps.remove(packageName);
   2048                 for (String s : mEverStoredApps) {
   2049                     known.writeUTF(s);
   2050                     if (MORE_DEBUG) Slog.v(TAG, "    " + s);
   2051                 }
   2052                 known.close();
   2053                 known = null;
   2054                 if (!tempKnownFile.renameTo(mEverStored)) {
   2055                     throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
   2056                 }
   2057             } catch (IOException e) {
   2058                 // Bad: we couldn't create the new copy.  For safety's sake we
   2059                 // abandon the whole process and remove all what's-backed-up
   2060                 // state entirely, meaning we'll force a backup pass for every
   2061                 // participant on the next boot or [re]install.
   2062                 Slog.w(TAG, "Error rewriting " + mEverStored, e);
   2063                 mEverStoredApps.clear();
   2064                 tempKnownFile.delete();
   2065                 mEverStored.delete();
   2066             } finally {
   2067                 try { if (known != null) known.close(); } catch (IOException e) {}
   2068             }
   2069         }
   2070     }
   2071 
   2072     // Persistently record the current and ancestral backup tokens as well
   2073     // as the set of packages with data [supposedly] available in the
   2074     // ancestral dataset.
   2075     void writeRestoreTokens() {
   2076         try {
   2077             RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
   2078 
   2079             // First, the version number of this record, for futureproofing
   2080             af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
   2081 
   2082             // Write the ancestral and current tokens
   2083             af.writeLong(mAncestralToken);
   2084             af.writeLong(mCurrentToken);
   2085 
   2086             // Now write the set of ancestral packages
   2087             if (mAncestralPackages == null) {
   2088                 af.writeInt(-1);
   2089             } else {
   2090                 af.writeInt(mAncestralPackages.size());
   2091                 if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
   2092                 for (String pkgName : mAncestralPackages) {
   2093                     af.writeUTF(pkgName);
   2094                     if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
   2095                 }
   2096             }
   2097             af.close();
   2098         } catch (IOException e) {
   2099             Slog.w(TAG, "Unable to write token file:", e);
   2100         }
   2101     }
   2102 
   2103     // Return the given transport
   2104     private IBackupTransport getTransport(String transportName) {
   2105         synchronized (mTransports) {
   2106             IBackupTransport transport = mTransports.get(transportName);
   2107             if (transport == null) {
   2108                 Slog.w(TAG, "Requested unavailable transport: " + transportName);
   2109             }
   2110             return transport;
   2111         }
   2112     }
   2113 
   2114     // fire off a backup agent, blocking until it attaches or times out
   2115     IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
   2116         IBackupAgent agent = null;
   2117         synchronized(mAgentConnectLock) {
   2118             mConnecting = true;
   2119             mConnectedAgent = null;
   2120             try {
   2121                 if (mActivityManager.bindBackupAgent(app, mode)) {
   2122                     Slog.d(TAG, "awaiting agent for " + app);
   2123 
   2124                     // success; wait for the agent to arrive
   2125                     // only wait 10 seconds for the bind to happen
   2126                     long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
   2127                     while (mConnecting && mConnectedAgent == null
   2128                             && (System.currentTimeMillis() < timeoutMark)) {
   2129                         try {
   2130                             mAgentConnectLock.wait(5000);
   2131                         } catch (InterruptedException e) {
   2132                             // just bail
   2133                             if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
   2134                             mActivityManager.clearPendingBackup();
   2135                             return null;
   2136                         }
   2137                     }
   2138 
   2139                     // if we timed out with no connect, abort and move on
   2140                     if (mConnecting == true) {
   2141                         Slog.w(TAG, "Timeout waiting for agent " + app);
   2142                         mActivityManager.clearPendingBackup();
   2143                         return null;
   2144                     }
   2145                     if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
   2146                     agent = mConnectedAgent;
   2147                 }
   2148             } catch (RemoteException e) {
   2149                 // can't happen - ActivityManager is local
   2150             }
   2151         }
   2152         return agent;
   2153     }
   2154 
   2155     // clear an application's data, blocking until the operation completes or times out
   2156     void clearApplicationDataSynchronous(String packageName) {
   2157         // Don't wipe packages marked allowClearUserData=false
   2158         try {
   2159             PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
   2160             if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
   2161                 if (MORE_DEBUG) Slog.i(TAG, "allowClearUserData=false so not wiping "
   2162                         + packageName);
   2163                 return;
   2164             }
   2165         } catch (NameNotFoundException e) {
   2166             Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
   2167             return;
   2168         }
   2169 
   2170         ClearDataObserver observer = new ClearDataObserver();
   2171 
   2172         synchronized(mClearDataLock) {
   2173             mClearingData = true;
   2174             try {
   2175                 mActivityManager.clearApplicationUserData(packageName, observer, 0);
   2176             } catch (RemoteException e) {
   2177                 // can't happen because the activity manager is in this process
   2178             }
   2179 
   2180             // only wait 10 seconds for the clear data to happen
   2181             long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
   2182             while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
   2183                 try {
   2184                     mClearDataLock.wait(5000);
   2185                 } catch (InterruptedException e) {
   2186                     // won't happen, but still.
   2187                     mClearingData = false;
   2188                 }
   2189             }
   2190         }
   2191     }
   2192 
   2193     class ClearDataObserver extends IPackageDataObserver.Stub {
   2194         public void onRemoveCompleted(String packageName, boolean succeeded) {
   2195             synchronized(mClearDataLock) {
   2196                 mClearingData = false;
   2197                 mClearDataLock.notifyAll();
   2198             }
   2199         }
   2200     }
   2201 
   2202     // Get the restore-set token for the best-available restore set for this package:
   2203     // the active set if possible, else the ancestral one.  Returns zero if none available.
   2204     long getAvailableRestoreToken(String packageName) {
   2205         long token = mAncestralToken;
   2206         synchronized (mQueueLock) {
   2207             if (mEverStoredApps.contains(packageName)) {
   2208                 token = mCurrentToken;
   2209             }
   2210         }
   2211         return token;
   2212     }
   2213 
   2214     // -----
   2215     // Interface and methods used by the asynchronous-with-timeout backup/restore operations
   2216 
   2217     interface BackupRestoreTask {
   2218         // Execute one tick of whatever state machine the task implements
   2219         void execute();
   2220 
   2221         // An operation that wanted a callback has completed
   2222         void operationComplete();
   2223 
   2224         // An operation that wanted a callback has timed out
   2225         void handleTimeout();
   2226     }
   2227 
   2228     void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) {
   2229         if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
   2230                 + " interval=" + interval);
   2231         synchronized (mCurrentOpLock) {
   2232             mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
   2233 
   2234             Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
   2235             mBackupHandler.sendMessageDelayed(msg, interval);
   2236         }
   2237     }
   2238 
   2239     // synchronous waiter case
   2240     boolean waitUntilOperationComplete(int token) {
   2241         if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
   2242                 + Integer.toHexString(token));
   2243         int finalState = OP_PENDING;
   2244         Operation op = null;
   2245         synchronized (mCurrentOpLock) {
   2246             while (true) {
   2247                 op = mCurrentOperations.get(token);
   2248                 if (op == null) {
   2249                     // mysterious disappearance: treat as success with no callback
   2250                     break;
   2251                 } else {
   2252                     if (op.state == OP_PENDING) {
   2253                         try {
   2254                             mCurrentOpLock.wait();
   2255                         } catch (InterruptedException e) {}
   2256                         // When the wait is notified we loop around and recheck the current state
   2257                     } else {
   2258                         // No longer pending; we're done
   2259                         finalState = op.state;
   2260                         break;
   2261                     }
   2262                 }
   2263             }
   2264         }
   2265 
   2266         mBackupHandler.removeMessages(MSG_TIMEOUT);
   2267         if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
   2268                 + " complete: finalState=" + finalState);
   2269         return finalState == OP_ACKNOWLEDGED;
   2270     }
   2271 
   2272     void handleTimeout(int token, Object obj) {
   2273         // Notify any synchronous waiters
   2274         Operation op = null;
   2275         synchronized (mCurrentOpLock) {
   2276             op = mCurrentOperations.get(token);
   2277             if (MORE_DEBUG) {
   2278                 if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
   2279                         + " but no op found");
   2280             }
   2281             int state = (op != null) ? op.state : OP_TIMEOUT;
   2282             if (state == OP_PENDING) {
   2283                 if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
   2284                 op.state = OP_TIMEOUT;
   2285                 mCurrentOperations.put(token, op);
   2286             }
   2287             mCurrentOpLock.notifyAll();
   2288         }
   2289 
   2290         // If there's a TimeoutHandler for this event, call it
   2291         if (op != null && op.callback != null) {
   2292             op.callback.handleTimeout();
   2293         }
   2294     }
   2295 
   2296     // ----- Back up a set of applications via a worker thread -----
   2297 
   2298     enum BackupState {
   2299         INITIAL,
   2300         RUNNING_QUEUE,
   2301         FINAL
   2302     }
   2303 
   2304     class PerformBackupTask implements BackupRestoreTask {
   2305         private static final String TAG = "PerformBackupTask";
   2306 
   2307         IBackupTransport mTransport;
   2308         ArrayList<BackupRequest> mQueue;
   2309         ArrayList<BackupRequest> mOriginalQueue;
   2310         File mStateDir;
   2311         File mJournal;
   2312         BackupState mCurrentState;
   2313 
   2314         // carried information about the current in-flight operation
   2315         IBackupAgent mAgentBinder;
   2316         PackageInfo mCurrentPackage;
   2317         File mSavedStateName;
   2318         File mBackupDataName;
   2319         File mNewStateName;
   2320         ParcelFileDescriptor mSavedState;
   2321         ParcelFileDescriptor mBackupData;
   2322         ParcelFileDescriptor mNewState;
   2323         int mStatus;
   2324         boolean mFinished;
   2325 
   2326         public PerformBackupTask(IBackupTransport transport, String dirName,
   2327                 ArrayList<BackupRequest> queue, File journal) {
   2328             mTransport = transport;
   2329             mOriginalQueue = queue;
   2330             mJournal = journal;
   2331 
   2332             mStateDir = new File(mBaseStateDir, dirName);
   2333 
   2334             mCurrentState = BackupState.INITIAL;
   2335             mFinished = false;
   2336 
   2337             addBackupTrace("STATE => INITIAL");
   2338         }
   2339 
   2340         // Main entry point: perform one chunk of work, updating the state as appropriate
   2341         // and reposting the next chunk to the primary backup handler thread.
   2342         @Override
   2343         public void execute() {
   2344             switch (mCurrentState) {
   2345                 case INITIAL:
   2346                     beginBackup();
   2347                     break;
   2348 
   2349                 case RUNNING_QUEUE:
   2350                     invokeNextAgent();
   2351                     break;
   2352 
   2353                 case FINAL:
   2354                     if (!mFinished) finalizeBackup();
   2355                     else {
   2356                         Slog.e(TAG, "Duplicate finish");
   2357                     }
   2358                     mFinished = true;
   2359                     break;
   2360             }
   2361         }
   2362 
   2363         // We're starting a backup pass.  Initialize the transport and send
   2364         // the PM metadata blob if we haven't already.
   2365         void beginBackup() {
   2366             if (DEBUG_BACKUP_TRACE) {
   2367                 clearBackupTrace();
   2368                 StringBuilder b = new StringBuilder(256);
   2369                 b.append("beginBackup: [");
   2370                 for (BackupRequest req : mOriginalQueue) {
   2371                     b.append(' ');
   2372                     b.append(req.packageName);
   2373                 }
   2374                 b.append(" ]");
   2375                 addBackupTrace(b.toString());
   2376             }
   2377 
   2378             mAgentBinder = null;
   2379             mStatus = BackupTransport.TRANSPORT_OK;
   2380 
   2381             // Sanity check: if the queue is empty we have no work to do.
   2382             if (mOriginalQueue.isEmpty()) {
   2383                 Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
   2384                 addBackupTrace("queue empty at begin");
   2385                 executeNextState(BackupState.FINAL);
   2386                 return;
   2387             }
   2388 
   2389             // We need to retain the original queue contents in case of transport
   2390             // failure, but we want a working copy that we can manipulate along
   2391             // the way.
   2392             mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
   2393 
   2394             if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
   2395 
   2396             File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
   2397             try {
   2398                 final String transportName = mTransport.transportDirName();
   2399                 EventLog.writeEvent(EventLogTags.BACKUP_START, transportName);
   2400 
   2401                 // If we haven't stored package manager metadata yet, we must init the transport.
   2402                 if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) {
   2403                     Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
   2404                     addBackupTrace("initializing transport " + transportName);
   2405                     resetBackupState(mStateDir);  // Just to make sure.
   2406                     mStatus = mTransport.initializeDevice();
   2407 
   2408                     addBackupTrace("transport.initializeDevice() == " + mStatus);
   2409                     if (mStatus == BackupTransport.TRANSPORT_OK) {
   2410                         EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
   2411                     } else {
   2412                         EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
   2413                         Slog.e(TAG, "Transport error in initializeDevice()");
   2414                     }
   2415                 }
   2416 
   2417                 // The package manager doesn't have a proper <application> etc, but since
   2418                 // it's running here in the system process we can just set up its agent
   2419                 // directly and use a synthetic BackupRequest.  We always run this pass
   2420                 // because it's cheap and this way we guarantee that we don't get out of
   2421                 // step even if we're selecting among various transports at run time.
   2422                 if (mStatus == BackupTransport.TRANSPORT_OK) {
   2423                     PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
   2424                             mPackageManager);
   2425                     mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
   2426                             IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
   2427                     addBackupTrace("PMBA invoke: " + mStatus);
   2428 
   2429                     // Because the PMBA is a local instance, it has already executed its
   2430                     // backup callback and returned.  Blow away the lingering (spurious)
   2431                     // pending timeout message for it.
   2432                     mBackupHandler.removeMessages(MSG_TIMEOUT);
   2433                 }
   2434 
   2435                 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
   2436                     // The backend reports that our dataset has been wiped.  Note this in
   2437                     // the event log; the no-success code below will reset the backup
   2438                     // state as well.
   2439                     EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
   2440                 }
   2441             } catch (Exception e) {
   2442                 Slog.e(TAG, "Error in backup thread", e);
   2443                 addBackupTrace("Exception in backup thread: " + e);
   2444                 mStatus = BackupTransport.TRANSPORT_ERROR;
   2445             } finally {
   2446                 // If we've succeeded so far, invokeAgentForBackup() will have run the PM
   2447                 // metadata and its completion/timeout callback will continue the state
   2448                 // machine chain.  If it failed that won't happen; we handle that now.
   2449                 addBackupTrace("exiting prelim: " + mStatus);
   2450                 if (mStatus != BackupTransport.TRANSPORT_OK) {
   2451                     // if things went wrong at this point, we need to
   2452                     // restage everything and try again later.
   2453                     resetBackupState(mStateDir);  // Just to make sure.
   2454                     executeNextState(BackupState.FINAL);
   2455                 }
   2456             }
   2457         }
   2458 
   2459         // Transport has been initialized and the PM metadata submitted successfully
   2460         // if that was warranted.  Now we process the single next thing in the queue.
   2461         void invokeNextAgent() {
   2462             mStatus = BackupTransport.TRANSPORT_OK;
   2463             addBackupTrace("invoke q=" + mQueue.size());
   2464 
   2465             // Sanity check that we have work to do.  If not, skip to the end where
   2466             // we reestablish the wakelock invariants etc.
   2467             if (mQueue.isEmpty()) {
   2468                 if (DEBUG) Slog.i(TAG, "queue now empty");
   2469                 executeNextState(BackupState.FINAL);
   2470                 return;
   2471             }
   2472 
   2473             // pop the entry we're going to process on this step
   2474             BackupRequest request = mQueue.get(0);
   2475             mQueue.remove(0);
   2476 
   2477             Slog.d(TAG, "starting agent for backup of " + request);
   2478             addBackupTrace("launch agent for " + request.packageName);
   2479 
   2480             // Verify that the requested app exists; it might be something that
   2481             // requested a backup but was then uninstalled.  The request was
   2482             // journalled and rather than tamper with the journal it's safer
   2483             // to sanity-check here.  This also gives us the classname of the
   2484             // package's backup agent.
   2485             try {
   2486                 mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
   2487                         PackageManager.GET_SIGNATURES);
   2488                 if (mCurrentPackage.applicationInfo.backupAgentName == null) {
   2489                     // The manifest has changed but we had a stale backup request pending.
   2490                     // This won't happen again because the app won't be requesting further
   2491                     // backups.
   2492                     Slog.i(TAG, "Package " + request.packageName
   2493                             + " no longer supports backup; skipping");
   2494                     addBackupTrace("skipping - no agent, completion is noop");
   2495                     executeNextState(BackupState.RUNNING_QUEUE);
   2496                     return;
   2497                 }
   2498 
   2499                 if ((mCurrentPackage.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
   2500                     // The app has been force-stopped or cleared or just installed,
   2501                     // and not yet launched out of that state, so just as it won't
   2502                     // receive broadcasts, we won't run it for backup.
   2503                     addBackupTrace("skipping - stopped");
   2504                     executeNextState(BackupState.RUNNING_QUEUE);
   2505                     return;
   2506                 }
   2507 
   2508                 IBackupAgent agent = null;
   2509                 try {
   2510                     mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
   2511                     agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
   2512                             IApplicationThread.BACKUP_MODE_INCREMENTAL);
   2513                     addBackupTrace("agent bound; a? = " + (agent != null));
   2514                     if (agent != null) {
   2515                         mAgentBinder = agent;
   2516                         mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
   2517                         // at this point we'll either get a completion callback from the
   2518                         // agent, or a timeout message on the main handler.  either way, we're
   2519                         // done here as long as we're successful so far.
   2520                     } else {
   2521                         // Timeout waiting for the agent
   2522                         mStatus = BackupTransport.AGENT_ERROR;
   2523                     }
   2524                 } catch (SecurityException ex) {
   2525                     // Try for the next one.
   2526                     Slog.d(TAG, "error in bind/backup", ex);
   2527                     mStatus = BackupTransport.AGENT_ERROR;
   2528                             addBackupTrace("agent SE");
   2529                 }
   2530             } catch (NameNotFoundException e) {
   2531                 Slog.d(TAG, "Package does not exist; skipping");
   2532                 addBackupTrace("no such package");
   2533                 mStatus = BackupTransport.AGENT_UNKNOWN;
   2534             } finally {
   2535                 mWakelock.setWorkSource(null);
   2536 
   2537                 // If there was an agent error, no timeout/completion handling will occur.
   2538                 // That means we need to direct to the next state ourselves.
   2539                 if (mStatus != BackupTransport.TRANSPORT_OK) {
   2540                     BackupState nextState = BackupState.RUNNING_QUEUE;
   2541                     mAgentBinder = null;
   2542 
   2543                     // An agent-level failure means we reenqueue this one agent for
   2544                     // a later retry, but otherwise proceed normally.
   2545                     if (mStatus == BackupTransport.AGENT_ERROR) {
   2546                         if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
   2547                                 + " - restaging");
   2548                         dataChangedImpl(request.packageName);
   2549                         mStatus = BackupTransport.TRANSPORT_OK;
   2550                         if (mQueue.isEmpty()) nextState = BackupState.FINAL;
   2551                     } else if (mStatus == BackupTransport.AGENT_UNKNOWN) {
   2552                         // Failed lookup of the app, so we couldn't bring up an agent, but
   2553                         // we're otherwise fine.  Just drop it and go on to the next as usual.
   2554                         mStatus = BackupTransport.TRANSPORT_OK;
   2555                     } else {
   2556                         // Transport-level failure means we reenqueue everything
   2557                         revertAndEndBackup();
   2558                         nextState = BackupState.FINAL;
   2559                     }
   2560 
   2561                     executeNextState(nextState);
   2562                 } else {
   2563                     // success case
   2564                     addBackupTrace("expecting completion/timeout callback");
   2565                 }
   2566             }
   2567         }
   2568 
   2569         void finalizeBackup() {
   2570             addBackupTrace("finishing");
   2571 
   2572             // Either backup was successful, in which case we of course do not need
   2573             // this pass's journal any more; or it failed, in which case we just
   2574             // re-enqueued all of these packages in the current active journal.
   2575             // Either way, we no longer need this pass's journal.
   2576             if (mJournal != null && !mJournal.delete()) {
   2577                 Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
   2578             }
   2579 
   2580             // If everything actually went through and this is the first time we've
   2581             // done a backup, we can now record what the current backup dataset token
   2582             // is.
   2583             if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) {
   2584                 addBackupTrace("success; recording token");
   2585                 try {
   2586                     mCurrentToken = mTransport.getCurrentRestoreSet();
   2587                     writeRestoreTokens();
   2588                 } catch (RemoteException e) {
   2589                     // nothing for it at this point, unfortunately, but this will be
   2590                     // recorded the next time we fully succeed.
   2591                     addBackupTrace("transport threw returning token");
   2592                 }
   2593             }
   2594 
   2595             // Set up the next backup pass - at this point we can set mBackupRunning
   2596             // to false to allow another pass to fire, because we're done with the
   2597             // state machine sequence and the wakelock is refcounted.
   2598             synchronized (mQueueLock) {
   2599                 mBackupRunning = false;
   2600                 if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
   2601                     // Make sure we back up everything and perform the one-time init
   2602                     clearMetadata();
   2603                     if (DEBUG) Slog.d(TAG, "Server requires init; rerunning");
   2604                     addBackupTrace("init required; rerunning");
   2605                     backupNow();
   2606                 }
   2607             }
   2608 
   2609             // Only once we're entirely finished do we release the wakelock
   2610             clearBackupTrace();
   2611             Slog.i(BackupManagerService.TAG, "Backup pass finished.");
   2612             mWakelock.release();
   2613         }
   2614 
   2615         // Remove the PM metadata state. This will generate an init on the next pass.
   2616         void clearMetadata() {
   2617             final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
   2618             if (pmState.exists()) pmState.delete();
   2619         }
   2620 
   2621         // Invoke an agent's doBackup() and start a timeout message spinning on the main
   2622         // handler in case it doesn't get back to us.
   2623         int invokeAgentForBackup(String packageName, IBackupAgent agent,
   2624                 IBackupTransport transport) {
   2625             if (DEBUG) Slog.d(TAG, "invokeAgentForBackup on " + packageName);
   2626             addBackupTrace("invoking " + packageName);
   2627 
   2628             mSavedStateName = new File(mStateDir, packageName);
   2629             mBackupDataName = new File(mDataDir, packageName + ".data");
   2630             mNewStateName = new File(mStateDir, packageName + ".new");
   2631             if (MORE_DEBUG) Slog.d(TAG, "data file: " + mBackupDataName);
   2632 
   2633             mSavedState = null;
   2634             mBackupData = null;
   2635             mNewState = null;
   2636 
   2637             final int token = generateToken();
   2638             try {
   2639                 // Look up the package info & signatures.  This is first so that if it
   2640                 // throws an exception, there's no file setup yet that would need to
   2641                 // be unraveled.
   2642                 if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
   2643                     // The metadata 'package' is synthetic; construct one and make
   2644                     // sure our global state is pointed at it
   2645                     mCurrentPackage = new PackageInfo();
   2646                     mCurrentPackage.packageName = packageName;
   2647                 }
   2648 
   2649                 // In a full backup, we pass a null ParcelFileDescriptor as
   2650                 // the saved-state "file". This is by definition an incremental,
   2651                 // so we build a saved state file to pass.
   2652                 mSavedState = ParcelFileDescriptor.open(mSavedStateName,
   2653                         ParcelFileDescriptor.MODE_READ_ONLY |
   2654                         ParcelFileDescriptor.MODE_CREATE);  // Make an empty file if necessary
   2655 
   2656                 mBackupData = ParcelFileDescriptor.open(mBackupDataName,
   2657                         ParcelFileDescriptor.MODE_READ_WRITE |
   2658                         ParcelFileDescriptor.MODE_CREATE |
   2659                         ParcelFileDescriptor.MODE_TRUNCATE);
   2660 
   2661                 if (!SELinux.restorecon(mBackupDataName)) {
   2662                     Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
   2663                 }
   2664 
   2665                 mNewState = ParcelFileDescriptor.open(mNewStateName,
   2666                         ParcelFileDescriptor.MODE_READ_WRITE |
   2667                         ParcelFileDescriptor.MODE_CREATE |
   2668                         ParcelFileDescriptor.MODE_TRUNCATE);
   2669 
   2670                 // Initiate the target's backup pass
   2671                 addBackupTrace("setting timeout");
   2672                 prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
   2673                 addBackupTrace("calling agent doBackup()");
   2674                 agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
   2675             } catch (Exception e) {
   2676                 Slog.e(TAG, "Error invoking for backup on " + packageName);
   2677                 addBackupTrace("exception: " + e);
   2678                 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
   2679                         e.toString());
   2680                 agentErrorCleanup();
   2681                 return BackupTransport.AGENT_ERROR;
   2682             }
   2683 
   2684             // At this point the agent is off and running.  The next thing to happen will
   2685             // either be a callback from the agent, at which point we'll process its data
   2686             // for transport, or a timeout.  Either way the next phase will happen in
   2687             // response to the TimeoutHandler interface callbacks.
   2688             addBackupTrace("invoke success");
   2689             return BackupTransport.TRANSPORT_OK;
   2690         }
   2691 
   2692         public void failAgent(IBackupAgent agent, String message) {
   2693             try {
   2694                 agent.fail(message);
   2695             } catch (Exception e) {
   2696                 Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName);
   2697             }
   2698         }
   2699 
   2700         // SHA-1 a byte array and return the result in hex
   2701         private String SHA1Checksum(byte[] input) {
   2702             final byte[] checksum;
   2703             try {
   2704                 MessageDigest md = MessageDigest.getInstance("SHA-1");
   2705                 checksum = md.digest(input);
   2706             } catch (NoSuchAlgorithmException e) {
   2707                 Slog.e(TAG, "Unable to use SHA-1!");
   2708                 return "00";
   2709             }
   2710 
   2711             StringBuffer sb = new StringBuffer(checksum.length * 2);
   2712             for (int i = 0; i < checksum.length; i++) {
   2713                 sb.append(Integer.toHexString(checksum[i]));
   2714             }
   2715             return sb.toString();
   2716         }
   2717 
   2718         private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
   2719                 throws IOException {
   2720             byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
   2721                     UserHandle.USER_OWNER);
   2722             // has the widget state changed since last time?
   2723             final File widgetFile = new File(mStateDir, pkgName + "_widget");
   2724             final boolean priorStateExists = widgetFile.exists();
   2725 
   2726             if (MORE_DEBUG) {
   2727                 if (priorStateExists || widgetState != null) {
   2728                     Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
   2729                             + " prior=" + priorStateExists);
   2730                 }
   2731             }
   2732 
   2733             if (!priorStateExists && widgetState == null) {
   2734                 // no prior state, no new state => nothing to do
   2735                 return;
   2736             }
   2737 
   2738             // if the new state is not null, we might need to compare checksums to
   2739             // determine whether to update the widget blob in the archive.  If the
   2740             // widget state *is* null, we know a priori at this point that we simply
   2741             // need to commit a deletion for it.
   2742             String newChecksum = null;
   2743             if (widgetState != null) {
   2744                 newChecksum = SHA1Checksum(widgetState);
   2745                 if (priorStateExists) {
   2746                     final String priorChecksum;
   2747                     try (
   2748                         FileInputStream fin = new FileInputStream(widgetFile);
   2749                         DataInputStream in = new DataInputStream(fin)
   2750                     ) {
   2751                         priorChecksum = in.readUTF();
   2752                     }
   2753                     if (Objects.equals(newChecksum, priorChecksum)) {
   2754                         // Same checksum => no state change => don't rewrite the widget data
   2755                         return;
   2756                     }
   2757                 }
   2758             } // else widget state *became* empty, so we need to commit a deletion
   2759 
   2760             BackupDataOutput out = new BackupDataOutput(fd);
   2761             if (widgetState != null) {
   2762                 try (
   2763                     FileOutputStream fout = new FileOutputStream(widgetFile);
   2764                     DataOutputStream stateOut = new DataOutputStream(fout)
   2765                 ) {
   2766                     stateOut.writeUTF(newChecksum);
   2767                 }
   2768 
   2769                 out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
   2770                 out.writeEntityData(widgetState, widgetState.length);
   2771             } else {
   2772                 // Widget state for this app has been removed; commit a deletion
   2773                 out.writeEntityHeader(KEY_WIDGET_STATE, -1);
   2774                 widgetFile.delete();
   2775             }
   2776         }
   2777 
   2778         @Override
   2779         public void operationComplete() {
   2780             // Okay, the agent successfully reported back to us!
   2781             final String pkgName = mCurrentPackage.packageName;
   2782             final long filepos = mBackupDataName.length();
   2783             FileDescriptor fd = mBackupData.getFileDescriptor();
   2784             try {
   2785                 // If it's a 3rd party app, see whether they wrote any protected keys
   2786                 // and complain mightily if they are attempting shenanigans.
   2787                 if (mCurrentPackage.applicationInfo != null &&
   2788                         (mCurrentPackage.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
   2789                     ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
   2790                             ParcelFileDescriptor.MODE_READ_ONLY);
   2791                     BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
   2792                     try {
   2793                         while (in.readNextHeader()) {
   2794                             final String key = in.getKey();
   2795                             if (key != null && key.charAt(0) >= 0xff00) {
   2796                                 // Not okay: crash them and bail.
   2797                                 failAgent(mAgentBinder, "Illegal backup key: " + key);
   2798                                 addBackupTrace("illegal key " + key + " from " + pkgName);
   2799                                 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
   2800                                         "bad key");
   2801                                 mBackupHandler.removeMessages(MSG_TIMEOUT);
   2802                                 agentErrorCleanup();
   2803                                 // agentErrorCleanup() implicitly executes next state properly
   2804                                 return;
   2805                             }
   2806                             in.skipEntityData();
   2807                         }
   2808                     } finally {
   2809                         if (readFd != null) {
   2810                             readFd.close();
   2811                         }
   2812                     }
   2813                 }
   2814 
   2815                 // Piggyback the widget state payload, if any
   2816                 writeWidgetPayloadIfAppropriate(fd, pkgName);
   2817             } catch (IOException e) {
   2818                 // Hard disk error; recovery/failure policy TBD.  For now roll back,
   2819                 // but we may want to consider this a transport-level failure (i.e.
   2820                 // we're in such a bad state that we can't contemplate doing backup
   2821                 // operations any more during this pass).
   2822                 Slog.w(TAG, "Unable to save widget state for " + pkgName);
   2823                 try {
   2824                     Os.ftruncate(fd, filepos);
   2825                 } catch (ErrnoException ee) {
   2826                     Slog.w(TAG, "Unable to roll back!");
   2827                 }
   2828             }
   2829 
   2830             // Spin the data off to the transport and proceed with the next stage.
   2831             if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
   2832                     + pkgName);
   2833             mBackupHandler.removeMessages(MSG_TIMEOUT);
   2834             clearAgentState();
   2835             addBackupTrace("operation complete");
   2836 
   2837             ParcelFileDescriptor backupData = null;
   2838             mStatus = BackupTransport.TRANSPORT_OK;
   2839             try {
   2840                 int size = (int) mBackupDataName.length();
   2841                 if (size > 0) {
   2842                     if (mStatus == BackupTransport.TRANSPORT_OK) {
   2843                         backupData = ParcelFileDescriptor.open(mBackupDataName,
   2844                                 ParcelFileDescriptor.MODE_READ_ONLY);
   2845                         addBackupTrace("sending data to transport");
   2846                         mStatus = mTransport.performBackup(mCurrentPackage, backupData);
   2847                     }
   2848 
   2849                     // TODO - We call finishBackup() for each application backed up, because
   2850                     // we need to know now whether it succeeded or failed.  Instead, we should
   2851                     // hold off on finishBackup() until the end, which implies holding off on
   2852                     // renaming *all* the output state files (see below) until that happens.
   2853 
   2854                     addBackupTrace("data delivered: " + mStatus);
   2855                     if (mStatus == BackupTransport.TRANSPORT_OK) {
   2856                         addBackupTrace("finishing op on transport");
   2857                         mStatus = mTransport.finishBackup();
   2858                         addBackupTrace("finished: " + mStatus);
   2859                     } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
   2860                         addBackupTrace("transport rejected package");
   2861                     }
   2862                 } else {
   2863                     if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
   2864                     addBackupTrace("no data to send");
   2865                 }
   2866 
   2867                 if (mStatus == BackupTransport.TRANSPORT_OK) {
   2868                     // After successful transport, delete the now-stale data
   2869                     // and juggle the files so that next time we supply the agent
   2870                     // with the new state file it just created.
   2871                     mBackupDataName.delete();
   2872                     mNewStateName.renameTo(mSavedStateName);
   2873                     EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
   2874                     logBackupComplete(pkgName);
   2875                 } else if (mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
   2876                     // The transport has rejected backup of this specific package.  Roll it
   2877                     // back but proceed with running the rest of the queue.
   2878                     mBackupDataName.delete();
   2879                     mNewStateName.delete();
   2880                     EventLogTags.writeBackupAgentFailure(pkgName, "Transport rejected");
   2881                 } else {
   2882                     // Actual transport-level failure to communicate the data to the backend
   2883                     EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
   2884                 }
   2885             } catch (Exception e) {
   2886                 Slog.e(TAG, "Transport error backing up " + pkgName, e);
   2887                 EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
   2888                 mStatus = BackupTransport.TRANSPORT_ERROR;
   2889             } finally {
   2890                 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
   2891             }
   2892 
   2893             final BackupState nextState;
   2894             if (mStatus == BackupTransport.TRANSPORT_OK
   2895                     || mStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
   2896                 // Success or single-package rejection.  Proceed with the next app if any,
   2897                 // otherwise we're done.
   2898                 nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
   2899             } else {
   2900                 // Any other error here indicates a transport-level failure.  That means
   2901                 // we need to halt everything and reschedule everything for next time.
   2902                 revertAndEndBackup();
   2903                 nextState = BackupState.FINAL;
   2904             }
   2905 
   2906             executeNextState(nextState);
   2907         }
   2908 
   2909         @Override
   2910         public void handleTimeout() {
   2911             // Whoops, the current agent timed out running doBackup().  Tidy up and restage
   2912             // it for the next time we run a backup pass.
   2913             // !!! TODO: keep track of failure counts per agent, and blacklist those which
   2914             // fail repeatedly (i.e. have proved themselves to be buggy).
   2915             Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
   2916             EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
   2917                     "timeout");
   2918             addBackupTrace("timeout of " + mCurrentPackage.packageName);
   2919             agentErrorCleanup();
   2920             dataChangedImpl(mCurrentPackage.packageName);
   2921         }
   2922 
   2923         void revertAndEndBackup() {
   2924             if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
   2925             addBackupTrace("transport error; reverting");
   2926             for (BackupRequest request : mOriginalQueue) {
   2927                 dataChangedImpl(request.packageName);
   2928             }
   2929             // We also want to reset the backup schedule based on whatever
   2930             // the transport suggests by way of retry/backoff time.
   2931             restartBackupAlarm();
   2932         }
   2933 
   2934         void agentErrorCleanup() {
   2935             mBackupDataName.delete();
   2936             mNewStateName.delete();
   2937             clearAgentState();
   2938 
   2939             executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
   2940         }
   2941 
   2942         // Cleanup common to both success and failure cases
   2943         void clearAgentState() {
   2944             try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
   2945             try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
   2946             try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
   2947             synchronized (mCurrentOpLock) {
   2948                 // Current-operation callback handling requires the validity of these various
   2949                 // bits of internal state as an invariant of the operation still being live.
   2950                 // This means we make sure to clear all of the state in unison inside the lock.
   2951                 mCurrentOperations.clear();
   2952                 mSavedState = mBackupData = mNewState = null;
   2953             }
   2954 
   2955             // If this was a pseudopackage there's no associated Activity Manager state
   2956             if (mCurrentPackage.applicationInfo != null) {
   2957                 addBackupTrace("unbinding " + mCurrentPackage.packageName);
   2958                 try {  // unbind even on timeout, just in case
   2959                     mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
   2960                 } catch (RemoteException e) { /* can't happen; activity manager is local */ }
   2961             }
   2962         }
   2963 
   2964         void restartBackupAlarm() {
   2965             addBackupTrace("setting backup trigger");
   2966             synchronized (mQueueLock) {
   2967                 try {
   2968                     startBackupAlarmsLocked(mTransport.requestBackupTime());
   2969                 } catch (RemoteException e) { /* cannot happen */ }
   2970             }
   2971         }
   2972 
   2973         void executeNextState(BackupState nextState) {
   2974             if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
   2975                     + this + " nextState=" + nextState);
   2976             addBackupTrace("executeNextState => " + nextState);
   2977             mCurrentState = nextState;
   2978             Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
   2979             mBackupHandler.sendMessage(msg);
   2980         }
   2981     }
   2982 
   2983 
   2984     // ----- Full backup/restore to a file/socket -----
   2985 
   2986     class FullBackupObbConnection implements ServiceConnection {
   2987         volatile IObbBackupService mService;
   2988 
   2989         FullBackupObbConnection() {
   2990             mService = null;
   2991         }
   2992 
   2993         public void establish() {
   2994             if (DEBUG) Slog.i(TAG, "Initiating bind of OBB service on " + this);
   2995             Intent obbIntent = new Intent().setComponent(new ComponentName(
   2996                     "com.android.sharedstoragebackup",
   2997                     "com.android.sharedstoragebackup.ObbBackupService"));
   2998             BackupManagerService.this.mContext.bindService(
   2999                     obbIntent, this, Context.BIND_AUTO_CREATE);
   3000         }
   3001 
   3002         public void tearDown() {
   3003             BackupManagerService.this.mContext.unbindService(this);
   3004         }
   3005 
   3006         public boolean backupObbs(PackageInfo pkg, OutputStream out) {
   3007             boolean success = false;
   3008             waitForConnection();
   3009 
   3010             ParcelFileDescriptor[] pipes = null;
   3011             try {
   3012                 pipes = ParcelFileDescriptor.createPipe();
   3013                 int token = generateToken();
   3014                 prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
   3015                 mService.backupObbs(pkg.packageName, pipes[1], token, mBackupManagerBinder);
   3016                 routeSocketDataToOutput(pipes[0], out);
   3017                 success = waitUntilOperationComplete(token);
   3018             } catch (Exception e) {
   3019                 Slog.w(TAG, "Unable to back up OBBs for " + pkg, e);
   3020             } finally {
   3021                 try {
   3022                     out.flush();
   3023                     if (pipes != null) {
   3024                         if (pipes[0] != null) pipes[0].close();
   3025                         if (pipes[1] != null) pipes[1].close();
   3026                     }
   3027                 } catch (IOException e) {
   3028                     Slog.w(TAG, "I/O error closing down OBB backup", e);
   3029                 }
   3030             }
   3031             return success;
   3032         }
   3033 
   3034         public void restoreObbFile(String pkgName, ParcelFileDescriptor data,
   3035                 long fileSize, int type, String path, long mode, long mtime,
   3036                 int token, IBackupManager callbackBinder) {
   3037             waitForConnection();
   3038 
   3039             try {
   3040                 mService.restoreObbFile(pkgName, data, fileSize, type, path, mode, mtime,
   3041                         token, callbackBinder);
   3042             } catch (Exception e) {
   3043                 Slog.w(TAG, "Unable to restore OBBs for " + pkgName, e);
   3044             }
   3045         }
   3046 
   3047         private void waitForConnection() {
   3048             synchronized (this) {
   3049                 while (mService == null) {
   3050                     if (DEBUG) Slog.i(TAG, "...waiting for OBB service binding...");
   3051                     try {
   3052                         this.wait();
   3053                     } catch (InterruptedException e) { /* never interrupted */ }
   3054                 }
   3055                 if (DEBUG) Slog.i(TAG, "Connected to OBB service; continuing");
   3056             }
   3057         }
   3058 
   3059         @Override
   3060         public void onServiceConnected(ComponentName name, IBinder service) {
   3061             synchronized (this) {
   3062                 mService = IObbBackupService.Stub.asInterface(service);
   3063                 if (DEBUG) Slog.i(TAG, "OBB service connection " + mService
   3064                         + " connected on " + this);
   3065                 this.notifyAll();
   3066             }
   3067         }
   3068 
   3069         @Override
   3070         public void onServiceDisconnected(ComponentName name) {
   3071             synchronized (this) {
   3072                 mService = null;
   3073                 if (DEBUG) Slog.i(TAG, "OBB service connection disconnected on " + this);
   3074                 this.notifyAll();
   3075             }
   3076         }
   3077 
   3078     }
   3079 
   3080     private void routeSocketDataToOutput(ParcelFileDescriptor inPipe, OutputStream out)
   3081             throws IOException {
   3082         FileInputStream raw = new FileInputStream(inPipe.getFileDescriptor());
   3083         DataInputStream in = new DataInputStream(raw);
   3084 
   3085         byte[] buffer = new byte[32 * 1024];
   3086         int chunkTotal;
   3087         while ((chunkTotal = in.readInt()) > 0) {
   3088             while (chunkTotal > 0) {
   3089                 int toRead = (chunkTotal > buffer.length) ? buffer.length : chunkTotal;
   3090                 int nRead = in.read(buffer, 0, toRead);
   3091                 out.write(buffer, 0, nRead);
   3092                 chunkTotal -= nRead;
   3093             }
   3094         }
   3095     }
   3096 
   3097     // Core logic for performing one package's full backup, gathering the tarball from the
   3098     // application and emitting it to the designated OutputStream.
   3099     class FullBackupEngine {
   3100         OutputStream mOutput;
   3101         IFullBackupRestoreObserver mObserver;
   3102         File mFilesDir;
   3103         File mManifestFile;
   3104         File mMetadataFile;
   3105         boolean mIncludeApks;
   3106 
   3107         class FullBackupRunner implements Runnable {
   3108             PackageInfo mPackage;
   3109             byte[] mWidgetData;
   3110             IBackupAgent mAgent;
   3111             ParcelFileDescriptor mPipe;
   3112             int mToken;
   3113             boolean mSendApk;
   3114             boolean mWriteManifest;
   3115 
   3116             FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
   3117                     int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
   3118                             throws IOException {
   3119                 mPackage = pack;
   3120                 mWidgetData = widgetData;
   3121                 mAgent = agent;
   3122                 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
   3123                 mToken = token;
   3124                 mSendApk = sendApk;
   3125                 mWriteManifest = writeManifest;
   3126             }
   3127 
   3128             @Override
   3129             public void run() {
   3130                 try {
   3131                     BackupDataOutput output = new BackupDataOutput(
   3132                             mPipe.getFileDescriptor());
   3133 
   3134                     if (mWriteManifest) {
   3135                         final boolean writeWidgetData = mWidgetData != null;
   3136                         if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
   3137                         writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
   3138                         FullBackup.backupToTar(mPackage.packageName, null, null,
   3139                                 mFilesDir.getAbsolutePath(),
   3140                                 mManifestFile.getAbsolutePath(),
   3141                                 output);
   3142                         mManifestFile.delete();
   3143 
   3144                         // We only need to write a metadata file if we have widget data to stash
   3145                         if (writeWidgetData) {
   3146                             writeMetadata(mPackage, mMetadataFile, mWidgetData);
   3147                             FullBackup.backupToTar(mPackage.packageName, null, null,
   3148                                     mFilesDir.getAbsolutePath(),
   3149                                     mMetadataFile.getAbsolutePath(),
   3150                                     output);
   3151                             mMetadataFile.delete();
   3152                         }
   3153                     }
   3154 
   3155                     if (mSendApk) {
   3156                         writeApkToBackup(mPackage, output);
   3157                     }
   3158 
   3159                     if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
   3160                     prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null);
   3161                     mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
   3162                 } catch (IOException e) {
   3163                     Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
   3164                 } catch (RemoteException e) {
   3165                     Slog.e(TAG, "Remote agent vanished during full backup of "
   3166                             +