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 android.app.backup;
     18 
     19 import android.annotation.RequiresPermission;
     20 import android.annotation.SystemApi;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.os.Bundle;
     24 import android.os.Handler;
     25 import android.os.Message;
     26 import android.os.RemoteException;
     27 import android.os.ServiceManager;
     28 import android.util.Log;
     29 import android.util.Pair;
     30 
     31 /**
     32  * The interface through which an application interacts with the Android backup service to
     33  * request backup and restore operations.
     34  * Applications instantiate it using the constructor and issue calls through that instance.
     35  * <p>
     36  * When an application has made changes to data which should be backed up, a
     37  * call to {@link #dataChanged()} will notify the backup service. The system
     38  * will then schedule a backup operation to occur in the near future. Repeated
     39  * calls to {@link #dataChanged()} have no further effect until the backup
     40  * operation actually occurs.
     41  * <p>
     42  * A backup or restore operation for your application begins when the system launches the
     43  * {@link android.app.backup.BackupAgent} subclass you've declared in your manifest. See the
     44  * documentation for {@link android.app.backup.BackupAgent} for a detailed description
     45  * of how the operation then proceeds.
     46  * <p>
     47  * Several attributes affecting the operation of the backup and restore mechanism
     48  * can be set on the <code>
     49  * <a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
     50  * tag in your application's AndroidManifest.xml file.
     51  *
     52  * <div class="special reference">
     53  * <h3>Developer Guides</h3>
     54  * <p>For more information about using BackupManager, read the
     55  * <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p></div>
     56  *
     57  * @attr ref android.R.styleable#AndroidManifestApplication_allowBackup
     58  * @attr ref android.R.styleable#AndroidManifestApplication_backupAgent
     59  * @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore
     60  * @attr ref android.R.styleable#AndroidManifestApplication_restoreAnyVersion
     61  */
     62 public class BackupManager {
     63     private static final String TAG = "BackupManager";
     64 
     65     // BackupObserver status codes
     66     /**
     67      * Indicates that backup succeeded.
     68      *
     69      * @hide
     70      */
     71     @SystemApi
     72     public static final int SUCCESS = 0;
     73 
     74     /**
     75      * Indicates that backup is either not enabled at all or
     76      * backup for the package was rejected by backup service
     77      * or backup transport,
     78      *
     79      * @hide
     80      */
     81     @SystemApi
     82     public static final int ERROR_BACKUP_NOT_ALLOWED = -2001;
     83 
     84     /**
     85      * The requested app is not installed on the device.
     86      *
     87      * @hide
     88      */
     89     @SystemApi
     90     public static final int ERROR_PACKAGE_NOT_FOUND = -2002;
     91 
     92     /**
     93      * The backup operation was cancelled.
     94      *
     95      * @hide
     96      */
     97     @SystemApi
     98     public static final int ERROR_BACKUP_CANCELLED = -2003;
     99 
    100     /**
    101      * The transport for some reason was not in a good state and
    102      * aborted the entire backup request. This is a transient
    103      * failure and should not be retried immediately.
    104      *
    105      * @hide
    106      */
    107     @SystemApi
    108     public static final int ERROR_TRANSPORT_ABORTED = BackupTransport.TRANSPORT_ERROR;
    109 
    110     /**
    111      * Returned when the transport was unable to process the
    112      * backup request for a given package, for example if the
    113      * transport hit a transient network failure. The remaining
    114      * packages provided to {@link #requestBackup(String[], BackupObserver)}
    115      * will still be attempted.
    116      *
    117      * @hide
    118      */
    119     @SystemApi
    120     public static final int ERROR_TRANSPORT_PACKAGE_REJECTED =
    121             BackupTransport.TRANSPORT_PACKAGE_REJECTED;
    122 
    123     /**
    124      * Returned when the transport reject the attempt to backup because
    125      * backup data size exceeded current quota limit for this package.
    126      *
    127      * @hide
    128      */
    129     @SystemApi
    130     public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED =
    131             BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
    132 
    133     /**
    134      * The {@link BackupAgent} for the requested package failed for some reason
    135      * and didn't provide appropriate backup data.
    136      *
    137      * @hide
    138      */
    139     @SystemApi
    140     public static final int ERROR_AGENT_FAILURE = BackupTransport.AGENT_ERROR;
    141 
    142     /**
    143      * Intent extra when any subsidiary backup-related UI is launched from Settings:  does
    144      * device policy or configuration permit backup operations to run at all?
    145      *
    146      * @hide
    147      */
    148     public static final String EXTRA_BACKUP_SERVICES_AVAILABLE = "backup_services_available";
    149 
    150     /**
    151      * If this flag is passed to {@link #requestBackup(String[], BackupObserver, int)},
    152      * BackupManager will pass a blank old state to BackupAgents of requested packages.
    153      *
    154      * @hide
    155      */
    156     @SystemApi
    157     public static final int FLAG_NON_INCREMENTAL_BACKUP = 1;
    158 
    159     /**
    160      * Use with {@link #requestBackup} to force backup of
    161      * package meta data. Typically you do not need to explicitly request this be backed up as it is
    162      * handled internally by the BackupManager. If you are requesting backups with
    163      * FLAG_NON_INCREMENTAL, this package won't automatically be backed up and you have to
    164      * explicitly request for its backup.
    165      *
    166      * @hide
    167      */
    168     @SystemApi
    169     public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
    170 
    171 
    172     /**
    173      * This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)}
    174      * if the requested transport is unavailable.
    175      *
    176      * @hide
    177      */
    178     @SystemApi
    179     public static final int ERROR_TRANSPORT_UNAVAILABLE = -1;
    180 
    181     /**
    182      * This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)} if the
    183      * requested transport is not a valid BackupTransport.
    184      *
    185      * @hide
    186      */
    187     @SystemApi
    188     public static final int ERROR_TRANSPORT_INVALID = -2;
    189 
    190     private Context mContext;
    191     private static IBackupManager sService;
    192 
    193     private static void checkServiceBinder() {
    194         if (sService == null) {
    195             sService = IBackupManager.Stub.asInterface(
    196                     ServiceManager.getService(Context.BACKUP_SERVICE));
    197         }
    198     }
    199 
    200     /**
    201      * Constructs a BackupManager object through which the application can
    202      * communicate with the Android backup system.
    203      *
    204      * @param context The {@link android.content.Context} that was provided when
    205      *                one of your application's {@link android.app.Activity Activities}
    206      *                was created.
    207      */
    208     public BackupManager(Context context) {
    209         mContext = context;
    210     }
    211 
    212     /**
    213      * Notifies the Android backup system that your application wishes to back up
    214      * new changes to its data.  A backup operation using your application's
    215      * {@link android.app.backup.BackupAgent} subclass will be scheduled when you
    216      * call this method.
    217      */
    218     public void dataChanged() {
    219         checkServiceBinder();
    220         if (sService != null) {
    221             try {
    222                 sService.dataChanged(mContext.getPackageName());
    223             } catch (RemoteException e) {
    224                 Log.d(TAG, "dataChanged() couldn't connect");
    225             }
    226         }
    227     }
    228 
    229     /**
    230      * Convenience method for callers who need to indicate that some other package
    231      * needs a backup pass.  This can be useful in the case of groups of packages
    232      * that share a uid.
    233      * <p>
    234      * This method requires that the application hold the "android.permission.BACKUP"
    235      * permission if the package named in the argument does not run under the same uid
    236      * as the caller.
    237      *
    238      * @param packageName The package name identifying the application to back up.
    239      */
    240     public static void dataChanged(String packageName) {
    241         checkServiceBinder();
    242         if (sService != null) {
    243             try {
    244                 sService.dataChanged(packageName);
    245             } catch (RemoteException e) {
    246                 Log.e(TAG, "dataChanged(pkg) couldn't connect");
    247             }
    248         }
    249     }
    250 
    251     /**
    252      * Restore the calling application from backup.  The data will be restored from the
    253      * current backup dataset if the application has stored data there, or from
    254      * the dataset used during the last full device setup operation if the current
    255      * backup dataset has no matching data.  If no backup data exists for this application
    256      * in either source, a nonzero value will be returned.
    257      *
    258      * <p>If this method returns zero (meaning success), the OS will attempt to retrieve
    259      * a backed-up dataset from the remote transport, instantiate the application's
    260      * backup agent, and pass the dataset to the agent's
    261      * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()}
    262      * method.
    263      *
    264      * @param observer The {@link RestoreObserver} to receive callbacks during the restore
    265      * operation. This must not be null.
    266      *
    267      * @return Zero on success; nonzero on error.
    268      */
    269     public int requestRestore(RestoreObserver observer) {
    270         return requestRestore(observer, null);
    271     }
    272 
    273     // system APIs start here
    274 
    275     /**
    276      * Restore the calling application from backup.  The data will be restored from the
    277      * current backup dataset if the application has stored data there, or from
    278      * the dataset used during the last full device setup operation if the current
    279      * backup dataset has no matching data.  If no backup data exists for this application
    280      * in either source, a nonzero value will be returned.
    281      *
    282      * <p>If this method returns zero (meaning success), the OS will attempt to retrieve
    283      * a backed-up dataset from the remote transport, instantiate the application's
    284      * backup agent, and pass the dataset to the agent's
    285      * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()}
    286      * method.
    287      *
    288      * @param observer The {@link RestoreObserver} to receive callbacks during the restore
    289      * operation. This must not be null.
    290      *
    291      * @param monitor the {@link BackupManagerMonitor} to receive callbacks during the restore
    292      * operation.
    293      *
    294      * @return Zero on success; nonzero on error.
    295      *
    296      * @hide
    297      */
    298     @SystemApi
    299     public int requestRestore(RestoreObserver observer, BackupManagerMonitor monitor) {
    300         int result = -1;
    301         checkServiceBinder();
    302         if (sService != null) {
    303             RestoreSession session = null;
    304             try {
    305                 IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(),
    306                     null);
    307                 if (binder != null) {
    308                     session = new RestoreSession(mContext, binder);
    309                     result = session.restorePackage(mContext.getPackageName(), observer, monitor);
    310                 }
    311             } catch (RemoteException e) {
    312                 Log.e(TAG, "restoreSelf() unable to contact service");
    313             } finally {
    314                 if (session != null) {
    315                     session.endRestoreSession();
    316                 }
    317             }
    318         }
    319         return result;
    320     }
    321 
    322     /**
    323      * Begin the process of restoring data from backup.  See the
    324      * {@link android.app.backup.RestoreSession} class for documentation on that process.
    325      * @hide
    326      */
    327     @SystemApi
    328     @RequiresPermission(android.Manifest.permission.BACKUP)
    329     public RestoreSession beginRestoreSession() {
    330         RestoreSession session = null;
    331         checkServiceBinder();
    332         if (sService != null) {
    333             try {
    334                 // All packages, current transport
    335                 IRestoreSession binder = sService.beginRestoreSession(null, null);
    336                 if (binder != null) {
    337                     session = new RestoreSession(mContext, binder);
    338                 }
    339             } catch (RemoteException e) {
    340                 Log.e(TAG, "beginRestoreSession() couldn't connect");
    341             }
    342         }
    343         return session;
    344     }
    345 
    346     /**
    347      * Enable/disable the backup service entirely.  When disabled, no backup
    348      * or restore operations will take place.  Data-changed notifications will
    349      * still be observed and collected, however, so that changes made while the
    350      * mechanism was disabled will still be backed up properly if it is enabled
    351      * at some point in the future.
    352      *
    353      * @hide
    354      */
    355     @SystemApi
    356     @RequiresPermission(android.Manifest.permission.BACKUP)
    357     public void setBackupEnabled(boolean isEnabled) {
    358         checkServiceBinder();
    359         if (sService != null) {
    360             try {
    361                 sService.setBackupEnabled(isEnabled);
    362             } catch (RemoteException e) {
    363                 Log.e(TAG, "setBackupEnabled() couldn't connect");
    364             }
    365         }
    366     }
    367 
    368     /**
    369      * Report whether the backup mechanism is currently enabled.
    370      *
    371      * @hide
    372      */
    373     @SystemApi
    374     @RequiresPermission(android.Manifest.permission.BACKUP)
    375     public boolean isBackupEnabled() {
    376         checkServiceBinder();
    377         if (sService != null) {
    378             try {
    379                 return sService.isBackupEnabled();
    380             } catch (RemoteException e) {
    381                 Log.e(TAG, "isBackupEnabled() couldn't connect");
    382             }
    383         }
    384         return false;
    385     }
    386 
    387     /**
    388      * Enable/disable data restore at application install time.  When enabled, app
    389      * installation will include an attempt to fetch the app's historical data from
    390      * the archival restore dataset (if any).  When disabled, no such attempt will
    391      * be made.
    392      *
    393      * @hide
    394      */
    395     @SystemApi
    396     @RequiresPermission(android.Manifest.permission.BACKUP)
    397     public void setAutoRestore(boolean isEnabled) {
    398         checkServiceBinder();
    399         if (sService != null) {
    400             try {
    401                 sService.setAutoRestore(isEnabled);
    402             } catch (RemoteException e) {
    403                 Log.e(TAG, "setAutoRestore() couldn't connect");
    404             }
    405         }
    406     }
    407 
    408     /**
    409      * Identify the currently selected transport.
    410      * @return The name of the currently active backup transport.  In case of
    411      *   failure or if no transport is currently active, this method returns {@code null}.
    412      *
    413      * @hide
    414      */
    415     @SystemApi
    416     @RequiresPermission(android.Manifest.permission.BACKUP)
    417     public String getCurrentTransport() {
    418         checkServiceBinder();
    419         if (sService != null) {
    420             try {
    421                 return sService.getCurrentTransport();
    422             } catch (RemoteException e) {
    423                 Log.e(TAG, "getCurrentTransport() couldn't connect");
    424             }
    425         }
    426         return null;
    427     }
    428 
    429     /**
    430      * Request a list of all available backup transports' names.
    431      *
    432      * @hide
    433      */
    434     @SystemApi
    435     @RequiresPermission(android.Manifest.permission.BACKUP)
    436     public String[] listAllTransports() {
    437         checkServiceBinder();
    438         if (sService != null) {
    439             try {
    440                 return sService.listAllTransports();
    441             } catch (RemoteException e) {
    442                 Log.e(TAG, "listAllTransports() couldn't connect");
    443             }
    444         }
    445         return null;
    446     }
    447 
    448     /**
    449      * Specify the current backup transport.
    450      *
    451      * @param transport The name of the transport to select.  This should be one
    452      *   of the names returned by {@link #listAllTransports()}. This is the String returned by
    453      *   {@link BackupTransport#name()} for the particular transport.
    454      * @return The name of the previously selected transport.  If the given transport
    455      *   name is not one of the currently available transports, no change is made to
    456      *   the current transport setting and the method returns null.
    457      *
    458      * @hide
    459      */
    460     @Deprecated
    461     @SystemApi
    462     @RequiresPermission(android.Manifest.permission.BACKUP)
    463     public String selectBackupTransport(String transport) {
    464         checkServiceBinder();
    465         if (sService != null) {
    466             try {
    467                 return sService.selectBackupTransport(transport);
    468             } catch (RemoteException e) {
    469                 Log.e(TAG, "selectBackupTransport() couldn't connect");
    470             }
    471         }
    472         return null;
    473     }
    474 
    475     /**
    476      * Specify the current backup transport and get notified when the transport is ready to be used.
    477      * This method is async because BackupManager might need to bind to the specified transport
    478      * which is in a separate process.
    479      *
    480      * @param transport ComponentName of the service hosting the transport. This is different from
    481      *                  the transport's name that is returned by {@link BackupTransport#name()}.
    482      * @param listener A listener object to get a callback on the transport being selected.
    483      *
    484      * @hide
    485      */
    486     @SystemApi
    487     @RequiresPermission(android.Manifest.permission.BACKUP)
    488     public void selectBackupTransport(ComponentName transport,
    489             SelectBackupTransportCallback listener) {
    490         checkServiceBinder();
    491         if (sService != null) {
    492             try {
    493                 SelectTransportListenerWrapper wrapper = listener == null ?
    494                         null : new SelectTransportListenerWrapper(mContext, listener);
    495                 sService.selectBackupTransportAsync(transport, wrapper);
    496             } catch (RemoteException e) {
    497                 Log.e(TAG, "selectBackupTransportAsync() couldn't connect");
    498             }
    499         }
    500     }
    501 
    502     /**
    503      * Schedule an immediate backup attempt for all pending key/value updates.  This
    504      * is primarily intended for transports to use when they detect a suitable
    505      * opportunity for doing a backup pass.  If there are no pending updates to
    506      * be sent, no action will be taken.  Even if some updates are pending, the
    507      * transport will still be asked to confirm via the usual requestBackupTime()
    508      * method.
    509      *
    510      * @hide
    511      */
    512     @SystemApi
    513     @RequiresPermission(android.Manifest.permission.BACKUP)
    514     public void backupNow() {
    515         checkServiceBinder();
    516         if (sService != null) {
    517             try {
    518                 sService.backupNow();
    519             } catch (RemoteException e) {
    520                 Log.e(TAG, "backupNow() couldn't connect");
    521             }
    522         }
    523     }
    524 
    525     /**
    526      * Ask the framework which dataset, if any, the given package's data would be
    527      * restored from if we were to install it right now.
    528      *
    529      * @param packageName The name of the package whose most-suitable dataset we
    530      *     wish to look up
    531      * @return The dataset token from which a restore should be attempted, or zero if
    532      *     no suitable data is available.
    533      *
    534      * @hide
    535      */
    536     @SystemApi
    537     @RequiresPermission(android.Manifest.permission.BACKUP)
    538     public long getAvailableRestoreToken(String packageName) {
    539         checkServiceBinder();
    540         if (sService != null) {
    541             try {
    542                 return sService.getAvailableRestoreToken(packageName);
    543             } catch (RemoteException e) {
    544                 Log.e(TAG, "getAvailableRestoreToken() couldn't connect");
    545             }
    546         }
    547         return 0;
    548     }
    549 
    550     /**
    551      * Ask the framework whether this app is eligible for backup.
    552      *
    553      * @param packageName The name of the package.
    554      * @return Whether this app is eligible for backup.
    555      *
    556      * @hide
    557      */
    558     @SystemApi
    559     @RequiresPermission(android.Manifest.permission.BACKUP)
    560     public boolean isAppEligibleForBackup(String packageName) {
    561         checkServiceBinder();
    562         if (sService != null) {
    563             try {
    564                 return sService.isAppEligibleForBackup(packageName);
    565             } catch (RemoteException e) {
    566                 Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect");
    567             }
    568         }
    569         return false;
    570     }
    571 
    572     /**
    573      * Request an immediate backup, providing an observer to which results of the backup operation
    574      * will be published. The Android backup system will decide for each package whether it will
    575      * be full app data backup or key/value-pair-based backup.
    576      *
    577      * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
    578      * provided packages using the remote transport.
    579      *
    580      * @param packages List of package names to backup.
    581      * @param observer The {@link BackupObserver} to receive callbacks during the backup
    582      * operation. Could be {@code null}.
    583      * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
    584      * @exception  IllegalArgumentException on null or empty {@code packages} param.
    585      *
    586      * @hide
    587      */
    588     @SystemApi
    589     @RequiresPermission(android.Manifest.permission.BACKUP)
    590     public int requestBackup(String[] packages, BackupObserver observer) {
    591         return requestBackup(packages, observer, null, 0);
    592     }
    593 
    594     /**
    595      * Request an immediate backup, providing an observer to which results of the backup operation
    596      * will be published. The Android backup system will decide for each package whether it will
    597      * be full app data backup or key/value-pair-based backup.
    598      *
    599      * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
    600      * provided packages using the remote transport.
    601      *
    602      * @param packages List of package names to backup.
    603      * @param observer The {@link BackupObserver} to receive callbacks during the backup
    604      *                 operation. Could be {@code null}.
    605      * @param monitor  The {@link BackupManagerMonitorWrapper} to receive callbacks of important
    606      *                 events during the backup operation. Could be {@code null}.
    607      * @param flags    {@link #FLAG_NON_INCREMENTAL_BACKUP}.
    608      * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
    609      * @throws IllegalArgumentException on null or empty {@code packages} param.
    610      * @hide
    611      */
    612     @SystemApi
    613     @RequiresPermission(android.Manifest.permission.BACKUP)
    614     public int requestBackup(String[] packages, BackupObserver observer,
    615             BackupManagerMonitor monitor, int flags) {
    616         checkServiceBinder();
    617         if (sService != null) {
    618             try {
    619                 BackupObserverWrapper observerWrapper = observer == null
    620                         ? null
    621                         : new BackupObserverWrapper(mContext, observer);
    622                 BackupManagerMonitorWrapper monitorWrapper = monitor == null
    623                         ? null
    624                         : new BackupManagerMonitorWrapper(monitor);
    625                 return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags);
    626             } catch (RemoteException e) {
    627                 Log.e(TAG, "requestBackup() couldn't connect");
    628             }
    629         }
    630         return -1;
    631     }
    632 
    633     /**
    634      * Cancel all running backups. After this call returns, no currently running backups will
    635      * interact with the selected transport.
    636      *
    637      * @hide
    638      */
    639     @SystemApi
    640     @RequiresPermission(android.Manifest.permission.BACKUP)
    641     public void cancelBackups() {
    642         checkServiceBinder();
    643         if (sService != null) {
    644             try {
    645                 sService.cancelBackups();
    646             } catch (RemoteException e) {
    647                 Log.e(TAG, "cancelBackups() couldn't connect.");
    648             }
    649         }
    650     }
    651 
    652     /*
    653      * We wrap incoming binder calls with a private class implementation that
    654      * redirects them into main-thread actions.  This serializes the backup
    655      * progress callbacks nicely within the usual main-thread lifecycle pattern.
    656      */
    657     @SystemApi
    658     private class BackupObserverWrapper extends IBackupObserver.Stub {
    659         final Handler mHandler;
    660         final BackupObserver mObserver;
    661 
    662         static final int MSG_UPDATE = 1;
    663         static final int MSG_RESULT = 2;
    664         static final int MSG_FINISHED = 3;
    665 
    666         BackupObserverWrapper(Context context, BackupObserver observer) {
    667             mHandler = new Handler(context.getMainLooper()) {
    668                 @Override
    669                 public void handleMessage(Message msg) {
    670                     switch (msg.what) {
    671                         case MSG_UPDATE:
    672                             Pair<String, BackupProgress> obj =
    673                                 (Pair<String, BackupProgress>) msg.obj;
    674                             mObserver.onUpdate(obj.first, obj.second);
    675                             break;
    676                         case MSG_RESULT:
    677                             mObserver.onResult((String)msg.obj, msg.arg1);
    678                             break;
    679                         case MSG_FINISHED:
    680                             mObserver.backupFinished(msg.arg1);
    681                             break;
    682                         default:
    683                             Log.w(TAG, "Unknown message: " + msg);
    684                             break;
    685                     }
    686                 }
    687             };
    688             mObserver = observer;
    689         }
    690 
    691         // Binder calls into this object just enqueue on the main-thread handler
    692         @Override
    693         public void onUpdate(String currentPackage, BackupProgress backupProgress) {
    694             mHandler.sendMessage(
    695                 mHandler.obtainMessage(MSG_UPDATE, Pair.create(currentPackage, backupProgress)));
    696         }
    697 
    698         @Override
    699         public void onResult(String currentPackage, int status) {
    700             mHandler.sendMessage(
    701                 mHandler.obtainMessage(MSG_RESULT, status, 0, currentPackage));
    702         }
    703 
    704         @Override
    705         public void backupFinished(int status) {
    706             mHandler.sendMessage(
    707                 mHandler.obtainMessage(MSG_FINISHED, status, 0));
    708         }
    709     }
    710 
    711     private class SelectTransportListenerWrapper extends ISelectBackupTransportCallback.Stub {
    712 
    713         private final Handler mHandler;
    714         private final SelectBackupTransportCallback mListener;
    715 
    716         SelectTransportListenerWrapper(Context context, SelectBackupTransportCallback listener) {
    717             mHandler = new Handler(context.getMainLooper());
    718             mListener = listener;
    719         }
    720 
    721         @Override
    722         public void onSuccess(final String transportName) {
    723             mHandler.post(new Runnable() {
    724                 @Override
    725                 public void run() {
    726                     mListener.onSuccess(transportName);
    727                 }
    728             });
    729         }
    730 
    731         @Override
    732         public void onFailure(final int reason) {
    733             mHandler.post(new Runnable() {
    734                 @Override
    735                 public void run() {
    736                     mListener.onFailure(reason);
    737                 }
    738             });
    739         }
    740     }
    741 
    742     private class BackupManagerMonitorWrapper extends IBackupManagerMonitor.Stub {
    743         final BackupManagerMonitor mMonitor;
    744 
    745         BackupManagerMonitorWrapper(BackupManagerMonitor monitor) {
    746             mMonitor = monitor;
    747         }
    748 
    749         @Override
    750         public void onEvent(final Bundle event) throws RemoteException {
    751             mMonitor.onEvent(event);
    752         }
    753     }
    754 
    755 }
    756