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
17package android.app.backup;
18
19import android.annotation.Nullable;
20import android.annotation.RequiresPermission;
21import android.annotation.SystemApi;
22import android.annotation.TestApi;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Message;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.os.UserHandle;
32import android.util.Log;
33import android.util.Pair;
34
35/**
36 * The interface through which an application interacts with the Android backup service to
37 * request backup and restore operations.
38 * Applications instantiate it using the constructor and issue calls through that instance.
39 * <p>
40 * When an application has made changes to data which should be backed up, a
41 * call to {@link #dataChanged()} will notify the backup service. The system
42 * will then schedule a backup operation to occur in the near future. Repeated
43 * calls to {@link #dataChanged()} have no further effect until the backup
44 * operation actually occurs.
45 * <p>
46 * A backup or restore operation for your application begins when the system launches the
47 * {@link android.app.backup.BackupAgent} subclass you've declared in your manifest. See the
48 * documentation for {@link android.app.backup.BackupAgent} for a detailed description
49 * of how the operation then proceeds.
50 * <p>
51 * Several attributes affecting the operation of the backup and restore mechanism
52 * can be set on the <code>
53 * <a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
54 * tag in your application's AndroidManifest.xml file.
55 *
56 * <div class="special reference">
57 * <h3>Developer Guides</h3>
58 * <p>For more information about using BackupManager, read the
59 * <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p></div>
60 *
61 * @attr ref android.R.styleable#AndroidManifestApplication_allowBackup
62 * @attr ref android.R.styleable#AndroidManifestApplication_backupAgent
63 * @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore
64 * @attr ref android.R.styleable#AndroidManifestApplication_restoreAnyVersion
65 */
66public class BackupManager {
67    private static final String TAG = "BackupManager";
68
69    // BackupObserver status codes
70    /**
71     * Indicates that backup succeeded.
72     *
73     * @hide
74     */
75    @SystemApi
76    public static final int SUCCESS = 0;
77
78    /**
79     * Indicates that backup is either not enabled at all or
80     * backup for the package was rejected by backup service
81     * or backup transport,
82     *
83     * @hide
84     */
85    @SystemApi
86    public static final int ERROR_BACKUP_NOT_ALLOWED = -2001;
87
88    /**
89     * The requested app is not installed on the device.
90     *
91     * @hide
92     */
93    @SystemApi
94    public static final int ERROR_PACKAGE_NOT_FOUND = -2002;
95
96    /**
97     * The backup operation was cancelled.
98     *
99     * @hide
100     */
101    @SystemApi
102    public static final int ERROR_BACKUP_CANCELLED = -2003;
103
104    /**
105     * The transport for some reason was not in a good state and
106     * aborted the entire backup request. This is a transient
107     * failure and should not be retried immediately.
108     *
109     * @hide
110     */
111    @SystemApi
112    public static final int ERROR_TRANSPORT_ABORTED = BackupTransport.TRANSPORT_ERROR;
113
114    /**
115     * Returned when the transport was unable to process the
116     * backup request for a given package, for example if the
117     * transport hit a transient network failure. The remaining
118     * packages provided to {@link #requestBackup(String[], BackupObserver)}
119     * will still be attempted.
120     *
121     * @hide
122     */
123    @SystemApi
124    public static final int ERROR_TRANSPORT_PACKAGE_REJECTED =
125            BackupTransport.TRANSPORT_PACKAGE_REJECTED;
126
127    /**
128     * Returned when the transport reject the attempt to backup because
129     * backup data size exceeded current quota limit for this package.
130     *
131     * @hide
132     */
133    @SystemApi
134    public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED =
135            BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
136
137    /**
138     * The {@link BackupAgent} for the requested package failed for some reason
139     * and didn't provide appropriate backup data.
140     *
141     * @hide
142     */
143    @SystemApi
144    public static final int ERROR_AGENT_FAILURE = BackupTransport.AGENT_ERROR;
145
146    /**
147     * Intent extra when any subsidiary backup-related UI is launched from Settings:  does
148     * device policy or configuration permit backup operations to run at all?
149     *
150     * @hide
151     */
152    public static final String EXTRA_BACKUP_SERVICES_AVAILABLE = "backup_services_available";
153
154    /**
155     * If this flag is passed to {@link #requestBackup(String[], BackupObserver, int)},
156     * BackupManager will pass a blank old state to BackupAgents of requested packages.
157     *
158     * @hide
159     */
160    @SystemApi
161    public static final int FLAG_NON_INCREMENTAL_BACKUP = 1;
162
163    /**
164     * Use with {@link #requestBackup} to force backup of
165     * package meta data. Typically you do not need to explicitly request this be backed up as it is
166     * handled internally by the BackupManager. If you are requesting backups with
167     * FLAG_NON_INCREMENTAL, this package won't automatically be backed up and you have to
168     * explicitly request for its backup.
169     *
170     * @hide
171     */
172    @SystemApi
173    public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
174
175
176    /**
177     * This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)}
178     * if the requested transport is unavailable.
179     *
180     * @hide
181     */
182    @SystemApi
183    public static final int ERROR_TRANSPORT_UNAVAILABLE = -1;
184
185    /**
186     * This error code is passed to {@link SelectBackupTransportCallback#onFailure(int)} if the
187     * requested transport is not a valid BackupTransport.
188     *
189     * @hide
190     */
191    @SystemApi
192    public static final int ERROR_TRANSPORT_INVALID = -2;
193
194    private Context mContext;
195    private static IBackupManager sService;
196
197    private static void checkServiceBinder() {
198        if (sService == null) {
199            sService = IBackupManager.Stub.asInterface(
200                    ServiceManager.getService(Context.BACKUP_SERVICE));
201        }
202    }
203
204    /**
205     * Constructs a BackupManager object through which the application can
206     * communicate with the Android backup system.
207     *
208     * @param context The {@link android.content.Context} that was provided when
209     *                one of your application's {@link android.app.Activity Activities}
210     *                was created.
211     */
212    public BackupManager(Context context) {
213        mContext = context;
214    }
215
216    /**
217     * Notifies the Android backup system that your application wishes to back up
218     * new changes to its data.  A backup operation using your application's
219     * {@link android.app.backup.BackupAgent} subclass will be scheduled when you
220     * call this method.
221     */
222    public void dataChanged() {
223        checkServiceBinder();
224        if (sService != null) {
225            try {
226                sService.dataChanged(mContext.getPackageName());
227            } catch (RemoteException e) {
228                Log.d(TAG, "dataChanged() couldn't connect");
229            }
230        }
231    }
232
233    /**
234     * Convenience method for callers who need to indicate that some other package
235     * needs a backup pass.  This can be useful in the case of groups of packages
236     * that share a uid.
237     * <p>
238     * This method requires that the application hold the "android.permission.BACKUP"
239     * permission if the package named in the argument does not run under the same uid
240     * as the caller.
241     *
242     * @param packageName The package name identifying the application to back up.
243     */
244    public static void dataChanged(String packageName) {
245        checkServiceBinder();
246        if (sService != null) {
247            try {
248                sService.dataChanged(packageName);
249            } catch (RemoteException e) {
250                Log.e(TAG, "dataChanged(pkg) couldn't connect");
251            }
252        }
253    }
254
255    /**
256     * @deprecated Applications shouldn't request a restore operation using this method. In Android
257     * P and later, this method is a no-op.
258     *
259     * <p>Restore the calling application from backup. The data will be restored from the
260     * current backup dataset if the application has stored data there, or from
261     * the dataset used during the last full device setup operation if the current
262     * backup dataset has no matching data.  If no backup data exists for this application
263     * in either source, a non-zero value is returned.
264     *
265     * <p>If this method returns zero (meaning success), the OS attempts to retrieve a backed-up
266     * dataset from the remote transport, instantiate the application's backup agent, and pass the
267     * dataset to the agent's
268     * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()}
269     * method.
270     *
271     * <p class="caution">Unlike other restore operations, this method doesn't terminate the
272     * application after the restore. The application continues running to receive the
273     * {@link RestoreObserver} callbacks on the {@code observer} argument. Full backups use an
274     * {@link android.app.Application Application} base class while key-value backups use the
275     * application subclass declared in the AndroidManifest.xml {@code <application>} tag.
276     *
277     * @param observer The {@link RestoreObserver} to receive callbacks during the restore
278     * operation. This must not be null.
279     *
280     * @return Zero on success; nonzero on error.
281     */
282    @Deprecated
283    public int requestRestore(RestoreObserver observer) {
284        return requestRestore(observer, null);
285    }
286
287    // system APIs start here
288
289    /**
290     * @deprecated Since Android P app can no longer request restoring of its backup.
291     *
292     * <p>Restore the calling application from backup.  The data will be restored from the
293     * current backup dataset if the application has stored data there, or from
294     * the dataset used during the last full device setup operation if the current
295     * backup dataset has no matching data.  If no backup data exists for this application
296     * in either source, a nonzero value will be returned.
297     *
298     * <p>If this method returns zero (meaning success), the OS will attempt to retrieve
299     * a backed-up dataset from the remote transport, instantiate the application's
300     * backup agent, and pass the dataset to the agent's
301     * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()}
302     * method.
303     *
304     * @param observer The {@link RestoreObserver} to receive callbacks during the restore
305     * operation. This must not be null.
306     *
307     * @param monitor the {@link BackupManagerMonitor} to receive callbacks during the restore
308     * operation.
309     *
310     * @return Zero on success; nonzero on error.
311     *
312     * @hide
313     */
314    @Deprecated
315    @SystemApi
316    public int requestRestore(RestoreObserver observer, BackupManagerMonitor monitor) {
317        Log.w(TAG, "requestRestore(): Since Android P app can no longer request restoring"
318                + " of its backup.");
319        return -1;
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     * Report whether the backup mechanism is currently active.
389     * When it is inactive, the device will not perform any backup operations, nor will it
390     * deliver data for restore, although clients can still safely call BackupManager methods.
391     *
392     * @hide
393     */
394    @SystemApi
395    @RequiresPermission(android.Manifest.permission.BACKUP)
396    public boolean isBackupServiceActive(UserHandle user) {
397        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
398                "isBackupServiceActive");
399        checkServiceBinder();
400        if (sService != null) {
401            try {
402                return sService.isBackupServiceActive(user.getIdentifier());
403            } catch (RemoteException e) {
404                Log.e(TAG, "isBackupEnabled() couldn't connect");
405            }
406        }
407        return false;
408    }
409
410    /**
411     * Enable/disable data restore at application install time.  When enabled, app
412     * installation will include an attempt to fetch the app's historical data from
413     * the archival restore dataset (if any).  When disabled, no such attempt will
414     * be made.
415     *
416     * @hide
417     */
418    @SystemApi
419    @RequiresPermission(android.Manifest.permission.BACKUP)
420    public void setAutoRestore(boolean isEnabled) {
421        checkServiceBinder();
422        if (sService != null) {
423            try {
424                sService.setAutoRestore(isEnabled);
425            } catch (RemoteException e) {
426                Log.e(TAG, "setAutoRestore() couldn't connect");
427            }
428        }
429    }
430
431    /**
432     * Identify the currently selected transport.
433     * @return The name of the currently active backup transport.  In case of
434     *   failure or if no transport is currently active, this method returns {@code null}.
435     *
436     * @hide
437     */
438    @SystemApi
439    @RequiresPermission(android.Manifest.permission.BACKUP)
440    public String getCurrentTransport() {
441        checkServiceBinder();
442        if (sService != null) {
443            try {
444                return sService.getCurrentTransport();
445            } catch (RemoteException e) {
446                Log.e(TAG, "getCurrentTransport() couldn't connect");
447            }
448        }
449        return null;
450    }
451
452    /**
453     * Request a list of all available backup transports' names.
454     *
455     * @hide
456     */
457    @SystemApi
458    @RequiresPermission(android.Manifest.permission.BACKUP)
459    public String[] listAllTransports() {
460        checkServiceBinder();
461        if (sService != null) {
462            try {
463                return sService.listAllTransports();
464            } catch (RemoteException e) {
465                Log.e(TAG, "listAllTransports() couldn't connect");
466            }
467        }
468        return null;
469    }
470
471    /**
472     * Update the attributes of the transport identified by {@code transportComponent}. If the
473     * specified transport has not been bound at least once (for registration), this call will be
474     * ignored. Only the host process of the transport can change its description, otherwise a
475     * {@link SecurityException} will be thrown.
476     *
477     * @param transportComponent The identity of the transport being described.
478     * @param name A {@link String} with the new name for the transport. This is NOT for
479     *     identification. MUST NOT be {@code null}.
480     * @param configurationIntent An {@link Intent} that can be passed to
481     *     {@link Context#startActivity} in order to launch the transport's configuration UI. It may
482     *     be {@code null} if the transport does not offer any user-facing configuration UI.
483     * @param currentDestinationString A {@link String} describing the destination to which the
484     *     transport is currently sending data. MUST NOT be {@code null}.
485     * @param dataManagementIntent An {@link Intent} that can be passed to
486     *     {@link Context#startActivity} in order to launch the transport's data-management UI. It
487     *     may be {@code null} if the transport does not offer any user-facing data
488     *     management UI.
489     * @param dataManagementLabel A {@link String} to be used as the label for the transport's data
490     *     management affordance. This MUST be {@code null} when dataManagementIntent is
491     *     {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
492     * @throws SecurityException If the UID of the calling process differs from the package UID of
493     *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
494     *
495     * @hide
496     */
497    @SystemApi
498    @RequiresPermission(android.Manifest.permission.BACKUP)
499    public void updateTransportAttributes(
500            ComponentName transportComponent,
501            String name,
502            @Nullable Intent configurationIntent,
503            String currentDestinationString,
504            @Nullable Intent dataManagementIntent,
505            @Nullable String dataManagementLabel) {
506        checkServiceBinder();
507        if (sService != null) {
508            try {
509                sService.updateTransportAttributes(
510                        transportComponent,
511                        name,
512                        configurationIntent,
513                        currentDestinationString,
514                        dataManagementIntent,
515                        dataManagementLabel);
516            } catch (RemoteException e) {
517                Log.e(TAG, "describeTransport() couldn't connect");
518            }
519        }
520    }
521
522    /**
523     * Specify the current backup transport.
524     *
525     * @param transport The name of the transport to select.  This should be one
526     *   of the names returned by {@link #listAllTransports()}. This is the String returned by
527     *   {@link BackupTransport#name()} for the particular transport.
528     * @return The name of the previously selected transport.  If the given transport
529     *   name is not one of the currently available transports, no change is made to
530     *   the current transport setting and the method returns null.
531     *
532     * @hide
533     */
534    @Deprecated
535    @SystemApi
536    @RequiresPermission(android.Manifest.permission.BACKUP)
537    public String selectBackupTransport(String transport) {
538        checkServiceBinder();
539        if (sService != null) {
540            try {
541                return sService.selectBackupTransport(transport);
542            } catch (RemoteException e) {
543                Log.e(TAG, "selectBackupTransport() couldn't connect");
544            }
545        }
546        return null;
547    }
548
549    /**
550     * Specify the current backup transport and get notified when the transport is ready to be used.
551     * This method is async because BackupManager might need to bind to the specified transport
552     * which is in a separate process.
553     *
554     * @param transport ComponentName of the service hosting the transport. This is different from
555     *                  the transport's name that is returned by {@link BackupTransport#name()}.
556     * @param listener A listener object to get a callback on the transport being selected.
557     *
558     * @hide
559     */
560    @SystemApi
561    @RequiresPermission(android.Manifest.permission.BACKUP)
562    public void selectBackupTransport(ComponentName transport,
563            SelectBackupTransportCallback listener) {
564        checkServiceBinder();
565        if (sService != null) {
566            try {
567                SelectTransportListenerWrapper wrapper = listener == null ?
568                        null : new SelectTransportListenerWrapper(mContext, listener);
569                sService.selectBackupTransportAsync(transport, wrapper);
570            } catch (RemoteException e) {
571                Log.e(TAG, "selectBackupTransportAsync() couldn't connect");
572            }
573        }
574    }
575
576    /**
577     * Schedule an immediate backup attempt for all pending key/value updates.  This
578     * is primarily intended for transports to use when they detect a suitable
579     * opportunity for doing a backup pass.  If there are no pending updates to
580     * be sent, no action will be taken.  Even if some updates are pending, the
581     * transport will still be asked to confirm via the usual requestBackupTime()
582     * method.
583     *
584     * @hide
585     */
586    @SystemApi
587    @RequiresPermission(android.Manifest.permission.BACKUP)
588    public void backupNow() {
589        checkServiceBinder();
590        if (sService != null) {
591            try {
592                sService.backupNow();
593            } catch (RemoteException e) {
594                Log.e(TAG, "backupNow() couldn't connect");
595            }
596        }
597    }
598
599    /**
600     * Ask the framework which dataset, if any, the given package's data would be
601     * restored from if we were to install it right now.
602     *
603     * @param packageName The name of the package whose most-suitable dataset we
604     *     wish to look up
605     * @return The dataset token from which a restore should be attempted, or zero if
606     *     no suitable data is available.
607     *
608     * @hide
609     */
610    @SystemApi
611    @RequiresPermission(android.Manifest.permission.BACKUP)
612    public long getAvailableRestoreToken(String packageName) {
613        checkServiceBinder();
614        if (sService != null) {
615            try {
616                return sService.getAvailableRestoreToken(packageName);
617            } catch (RemoteException e) {
618                Log.e(TAG, "getAvailableRestoreToken() couldn't connect");
619            }
620        }
621        return 0;
622    }
623
624    /**
625     * Ask the framework whether this app is eligible for backup.
626     *
627     * @param packageName The name of the package.
628     * @return Whether this app is eligible for backup.
629     *
630     * @hide
631     */
632    @SystemApi
633    @RequiresPermission(android.Manifest.permission.BACKUP)
634    public boolean isAppEligibleForBackup(String packageName) {
635        checkServiceBinder();
636        if (sService != null) {
637            try {
638                return sService.isAppEligibleForBackup(packageName);
639            } catch (RemoteException e) {
640                Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect");
641            }
642        }
643        return false;
644    }
645
646    /**
647     * Request an immediate backup, providing an observer to which results of the backup operation
648     * will be published. The Android backup system will decide for each package whether it will
649     * be full app data backup or key/value-pair-based backup.
650     *
651     * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
652     * provided packages using the remote transport.
653     *
654     * @param packages List of package names to backup.
655     * @param observer The {@link BackupObserver} to receive callbacks during the backup
656     * operation. Could be {@code null}.
657     * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
658     * @exception  IllegalArgumentException on null or empty {@code packages} param.
659     *
660     * @hide
661     */
662    @SystemApi
663    @RequiresPermission(android.Manifest.permission.BACKUP)
664    public int requestBackup(String[] packages, BackupObserver observer) {
665        return requestBackup(packages, observer, null, 0);
666    }
667
668    /**
669     * Request an immediate backup, providing an observer to which results of the backup operation
670     * will be published. The Android backup system will decide for each package whether it will
671     * be full app data backup or key/value-pair-based backup.
672     *
673     * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
674     * provided packages using the remote transport.
675     *
676     * @param packages List of package names to backup.
677     * @param observer The {@link BackupObserver} to receive callbacks during the backup
678     *                 operation. Could be {@code null}.
679     * @param monitor  The {@link BackupManagerMonitorWrapper} to receive callbacks of important
680     *                 events during the backup operation. Could be {@code null}.
681     * @param flags    {@link #FLAG_NON_INCREMENTAL_BACKUP}.
682     * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
683     * @throws IllegalArgumentException on null or empty {@code packages} param.
684     * @hide
685     */
686    @SystemApi
687    @RequiresPermission(android.Manifest.permission.BACKUP)
688    public int requestBackup(String[] packages, BackupObserver observer,
689            BackupManagerMonitor monitor, int flags) {
690        checkServiceBinder();
691        if (sService != null) {
692            try {
693                BackupObserverWrapper observerWrapper = observer == null
694                        ? null
695                        : new BackupObserverWrapper(mContext, observer);
696                BackupManagerMonitorWrapper monitorWrapper = monitor == null
697                        ? null
698                        : new BackupManagerMonitorWrapper(monitor);
699                return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags);
700            } catch (RemoteException e) {
701                Log.e(TAG, "requestBackup() couldn't connect");
702            }
703        }
704        return -1;
705    }
706
707    /**
708     * Cancel all running backups. After this call returns, no currently running backups will
709     * interact with the selected transport.
710     *
711     * @hide
712     */
713    @SystemApi
714    @RequiresPermission(android.Manifest.permission.BACKUP)
715    public void cancelBackups() {
716        checkServiceBinder();
717        if (sService != null) {
718            try {
719                sService.cancelBackups();
720            } catch (RemoteException e) {
721                Log.e(TAG, "cancelBackups() couldn't connect.");
722            }
723        }
724    }
725
726    /**
727     * Returns an {@link Intent} for the specified transport's configuration UI.
728     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
729     * Intent, String)}.
730     * @param transportName The name of the registered transport.
731     * @hide
732     */
733    @SystemApi
734    @TestApi
735    @RequiresPermission(android.Manifest.permission.BACKUP)
736    public Intent getConfigurationIntent(String transportName) {
737        if (sService != null) {
738            try {
739                return sService.getConfigurationIntent(transportName);
740            } catch (RemoteException e) {
741                Log.e(TAG, "getConfigurationIntent() couldn't connect");
742            }
743        }
744        return null;
745    }
746
747    /**
748     * Returns a {@link String} describing where the specified transport is sending data.
749     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
750     * Intent, String)}.
751     * @param transportName The name of the registered transport.
752     * @hide
753     */
754    @SystemApi
755    @TestApi
756    @RequiresPermission(android.Manifest.permission.BACKUP)
757    public String getDestinationString(String transportName) {
758        if (sService != null) {
759            try {
760                return sService.getDestinationString(transportName);
761            } catch (RemoteException e) {
762                Log.e(TAG, "getDestinationString() couldn't connect");
763            }
764        }
765        return null;
766    }
767
768    /**
769     * Returns an {@link Intent} for the specified transport's data management UI.
770     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
771     * Intent, String)}.
772     * @param transportName The name of the registered transport.
773     * @hide
774     */
775    @SystemApi
776    @TestApi
777    @RequiresPermission(android.Manifest.permission.BACKUP)
778    public Intent getDataManagementIntent(String transportName) {
779        if (sService != null) {
780            try {
781                return sService.getDataManagementIntent(transportName);
782            } catch (RemoteException e) {
783                Log.e(TAG, "getDataManagementIntent() couldn't connect");
784            }
785        }
786        return null;
787    }
788
789    /**
790     * Returns a {@link String} describing what the specified transport's data management intent is
791     * used for.
792     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
793     * Intent, String)}.
794     *
795     * @param transportName The name of the registered transport.
796     * @hide
797     */
798    @SystemApi
799    @TestApi
800    @RequiresPermission(android.Manifest.permission.BACKUP)
801    public String getDataManagementLabel(String transportName) {
802        if (sService != null) {
803            try {
804                return sService.getDataManagementLabel(transportName);
805            } catch (RemoteException e) {
806                Log.e(TAG, "getDataManagementLabel() couldn't connect");
807            }
808        }
809        return null;
810    }
811
812    /*
813     * We wrap incoming binder calls with a private class implementation that
814     * redirects them into main-thread actions.  This serializes the backup
815     * progress callbacks nicely within the usual main-thread lifecycle pattern.
816     */
817    private class BackupObserverWrapper extends IBackupObserver.Stub {
818        final Handler mHandler;
819        final BackupObserver mObserver;
820
821        static final int MSG_UPDATE = 1;
822        static final int MSG_RESULT = 2;
823        static final int MSG_FINISHED = 3;
824
825        BackupObserverWrapper(Context context, BackupObserver observer) {
826            mHandler = new Handler(context.getMainLooper()) {
827                @Override
828                public void handleMessage(Message msg) {
829                    switch (msg.what) {
830                        case MSG_UPDATE:
831                            Pair<String, BackupProgress> obj =
832                                (Pair<String, BackupProgress>) msg.obj;
833                            mObserver.onUpdate(obj.first, obj.second);
834                            break;
835                        case MSG_RESULT:
836                            mObserver.onResult((String)msg.obj, msg.arg1);
837                            break;
838                        case MSG_FINISHED:
839                            mObserver.backupFinished(msg.arg1);
840                            break;
841                        default:
842                            Log.w(TAG, "Unknown message: " + msg);
843                            break;
844                    }
845                }
846            };
847            mObserver = observer;
848        }
849
850        // Binder calls into this object just enqueue on the main-thread handler
851        @Override
852        public void onUpdate(String currentPackage, BackupProgress backupProgress) {
853            mHandler.sendMessage(
854                mHandler.obtainMessage(MSG_UPDATE, Pair.create(currentPackage, backupProgress)));
855        }
856
857        @Override
858        public void onResult(String currentPackage, int status) {
859            mHandler.sendMessage(
860                mHandler.obtainMessage(MSG_RESULT, status, 0, currentPackage));
861        }
862
863        @Override
864        public void backupFinished(int status) {
865            mHandler.sendMessage(
866                mHandler.obtainMessage(MSG_FINISHED, status, 0));
867        }
868    }
869
870    private class SelectTransportListenerWrapper extends ISelectBackupTransportCallback.Stub {
871
872        private final Handler mHandler;
873        private final SelectBackupTransportCallback mListener;
874
875        SelectTransportListenerWrapper(Context context, SelectBackupTransportCallback listener) {
876            mHandler = new Handler(context.getMainLooper());
877            mListener = listener;
878        }
879
880        @Override
881        public void onSuccess(final String transportName) {
882            mHandler.post(new Runnable() {
883                @Override
884                public void run() {
885                    mListener.onSuccess(transportName);
886                }
887            });
888        }
889
890        @Override
891        public void onFailure(final int reason) {
892            mHandler.post(new Runnable() {
893                @Override
894                public void run() {
895                    mListener.onFailure(reason);
896                }
897            });
898        }
899    }
900
901    private class BackupManagerMonitorWrapper extends IBackupManagerMonitor.Stub {
902        final BackupManagerMonitor mMonitor;
903
904        BackupManagerMonitorWrapper(BackupManagerMonitor monitor) {
905            mMonitor = monitor;
906        }
907
908        @Override
909        public void onEvent(final Bundle event) throws RemoteException {
910            mMonitor.onEvent(event);
911        }
912    }
913
914}
915