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.Nullable;
20 import android.annotation.RequiresPermission;
21 import android.annotation.SystemApi;
22 import android.annotation.TestApi;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.os.RemoteException;
30 import android.os.ServiceManager;
31 import android.os.UserHandle;
32 import android.util.Log;
33 import 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  */
66 public 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 
checkServiceBinder()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      */
BackupManager(Context context)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      */
dataChanged()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      */
dataChanged(String packageName)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
requestRestore(RestoreObserver observer)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
requestRestore(RestoreObserver observer, BackupManagerMonitor monitor)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)
beginRestoreSession()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)
setBackupEnabled(boolean isEnabled)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)
isBackupEnabled()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)
isBackupServiceActive(UserHandle user)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)
setAutoRestore(boolean isEnabled)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)
getCurrentTransport()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)
listAllTransports()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)
updateTransportAttributes( ComponentName transportComponent, String name, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, @Nullable String dataManagementLabel)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)
selectBackupTransport(String transport)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)
selectBackupTransport(ComponentName transport, SelectBackupTransportCallback listener)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)
backupNow()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)
getAvailableRestoreToken(String packageName)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)
isAppEligibleForBackup(String packageName)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)
requestBackup(String[] packages, BackupObserver observer)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)
requestBackup(String[] packages, BackupObserver observer, BackupManagerMonitor monitor, int flags)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)
cancelBackups()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)
getConfigurationIntent(String transportName)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)
getDestinationString(String transportName)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)
getDataManagementIntent(String transportName)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)
getDataManagementLabel(String transportName)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 
BackupObserverWrapper(Context context, BackupObserver observer)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
onUpdate(String currentPackage, BackupProgress backupProgress)852         public void onUpdate(String currentPackage, BackupProgress backupProgress) {
853             mHandler.sendMessage(
854                 mHandler.obtainMessage(MSG_UPDATE, Pair.create(currentPackage, backupProgress)));
855         }
856 
857         @Override
onResult(String currentPackage, int status)858         public void onResult(String currentPackage, int status) {
859             mHandler.sendMessage(
860                 mHandler.obtainMessage(MSG_RESULT, status, 0, currentPackage));
861         }
862 
863         @Override
backupFinished(int status)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 
SelectTransportListenerWrapper(Context context, SelectBackupTransportCallback listener)875         SelectTransportListenerWrapper(Context context, SelectBackupTransportCallback listener) {
876             mHandler = new Handler(context.getMainLooper());
877             mListener = listener;
878         }
879 
880         @Override
onSuccess(final String transportName)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
onFailure(final int reason)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 
BackupManagerMonitorWrapper(BackupManagerMonitor monitor)904         BackupManagerMonitorWrapper(BackupManagerMonitor monitor) {
905             mMonitor = monitor;
906         }
907 
908         @Override
onEvent(final Bundle event)909         public void onEvent(final Bundle event) throws RemoteException {
910             mMonitor.onEvent(event);
911         }
912     }
913 
914 }
915