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.SystemApi;
20 import android.content.Context;
21 import android.os.Handler;
22 import android.os.Message;
23 import android.os.RemoteException;
24 import android.os.ServiceManager;
25 import android.util.Log;
26 import android.util.Pair;
27 
28 /**
29  * The interface through which an application interacts with the Android backup service to
30  * request backup and restore operations.
31  * Applications instantiate it using the constructor and issue calls through that instance.
32  * <p>
33  * When an application has made changes to data which should be backed up, a
34  * call to {@link #dataChanged()} will notify the backup service. The system
35  * will then schedule a backup operation to occur in the near future. Repeated
36  * calls to {@link #dataChanged()} have no further effect until the backup
37  * operation actually occurs.
38  * <p>
39  * A backup or restore operation for your application begins when the system launches the
40  * {@link android.app.backup.BackupAgent} subclass you've declared in your manifest. See the
41  * documentation for {@link android.app.backup.BackupAgent} for a detailed description
42  * of how the operation then proceeds.
43  * <p>
44  * Several attributes affecting the operation of the backup and restore mechanism
45  * can be set on the <code>
46  * <a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
47  * tag in your application's AndroidManifest.xml file.
48  *
49  * <div class="special reference">
50  * <h3>Developer Guides</h3>
51  * <p>For more information about using BackupManager, read the
52  * <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p></div>
53  *
54  * @attr ref android.R.styleable#AndroidManifestApplication_allowBackup
55  * @attr ref android.R.styleable#AndroidManifestApplication_backupAgent
56  * @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore
57  * @attr ref android.R.styleable#AndroidManifestApplication_restoreAnyVersion
58  */
59 public class BackupManager {
60     private static final String TAG = "BackupManager";
61 
62     // BackupObserver status codes
63     /**
64      * Indicates that backup succeeded.
65      *
66      * @hide
67      */
68     @SystemApi
69     public static final int SUCCESS = 0;
70 
71     /**
72      * Indicates that backup is either not enabled at all or
73      * backup for the package was rejected by backup service
74      * or backup transport,
75      *
76      * @hide
77      */
78     @SystemApi
79     public static final int ERROR_BACKUP_NOT_ALLOWED = -2001;
80 
81     /**
82      * The requested app is not installed on the device.
83      *
84      * @hide
85      */
86     @SystemApi
87     public static final int ERROR_PACKAGE_NOT_FOUND = -2002;
88 
89     /**
90      * The transport for some reason was not in a good state and
91      * aborted the entire backup request. This is a transient
92      * failure and should not be retried immediately.
93      *
94      * @hide
95      */
96     @SystemApi
97     public static final int ERROR_TRANSPORT_ABORTED = BackupTransport.TRANSPORT_ERROR;
98 
99     /**
100      * Returned when the transport was unable to process the
101      * backup request for a given package, for example if the
102      * transport hit a transient network failure. The remaining
103      * packages provided to {@link #requestBackup(String[], BackupObserver)}
104      * will still be attempted.
105      *
106      * @hide
107      */
108     @SystemApi
109     public static final int ERROR_TRANSPORT_PACKAGE_REJECTED =
110             BackupTransport.TRANSPORT_PACKAGE_REJECTED;
111 
112     /**
113      * Returned when the transport reject the attempt to backup because
114      * backup data size exceeded current quota limit for this package.
115      *
116      * @hide
117      */
118     @SystemApi
119     public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED =
120             BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
121 
122     /**
123      * The {@link BackupAgent} for the requested package failed for some reason
124      * and didn't provide appropriate backup data.
125      *
126      * @hide
127      */
128     @SystemApi
129     public static final int ERROR_AGENT_FAILURE = BackupTransport.AGENT_ERROR;
130 
131     private Context mContext;
132     private static IBackupManager sService;
133 
checkServiceBinder()134     private static void checkServiceBinder() {
135         if (sService == null) {
136             sService = IBackupManager.Stub.asInterface(
137                     ServiceManager.getService(Context.BACKUP_SERVICE));
138         }
139     }
140 
141     /**
142      * Constructs a BackupManager object through which the application can
143      * communicate with the Android backup system.
144      *
145      * @param context The {@link android.content.Context} that was provided when
146      *                one of your application's {@link android.app.Activity Activities}
147      *                was created.
148      */
BackupManager(Context context)149     public BackupManager(Context context) {
150         mContext = context;
151     }
152 
153     /**
154      * Notifies the Android backup system that your application wishes to back up
155      * new changes to its data.  A backup operation using your application's
156      * {@link android.app.backup.BackupAgent} subclass will be scheduled when you
157      * call this method.
158      */
dataChanged()159     public void dataChanged() {
160         checkServiceBinder();
161         if (sService != null) {
162             try {
163                 sService.dataChanged(mContext.getPackageName());
164             } catch (RemoteException e) {
165                 Log.d(TAG, "dataChanged() couldn't connect");
166             }
167         }
168     }
169 
170     /**
171      * Convenience method for callers who need to indicate that some other package
172      * needs a backup pass.  This can be useful in the case of groups of packages
173      * that share a uid.
174      * <p>
175      * This method requires that the application hold the "android.permission.BACKUP"
176      * permission if the package named in the argument does not run under the same uid
177      * as the caller.
178      *
179      * @param packageName The package name identifying the application to back up.
180      */
dataChanged(String packageName)181     public static void dataChanged(String packageName) {
182         checkServiceBinder();
183         if (sService != null) {
184             try {
185                 sService.dataChanged(packageName);
186             } catch (RemoteException e) {
187                 Log.e(TAG, "dataChanged(pkg) couldn't connect");
188             }
189         }
190     }
191 
192     /**
193      * Restore the calling application from backup.  The data will be restored from the
194      * current backup dataset if the application has stored data there, or from
195      * the dataset used during the last full device setup operation if the current
196      * backup dataset has no matching data.  If no backup data exists for this application
197      * in either source, a nonzero value will be returned.
198      *
199      * <p>If this method returns zero (meaning success), the OS will attempt to retrieve
200      * a backed-up dataset from the remote transport, instantiate the application's
201      * backup agent, and pass the dataset to the agent's
202      * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()}
203      * method.
204      *
205      * @param observer The {@link RestoreObserver} to receive callbacks during the restore
206      * operation. This must not be null.
207      *
208      * @return Zero on success; nonzero on error.
209      */
requestRestore(RestoreObserver observer)210     public int requestRestore(RestoreObserver observer) {
211         int result = -1;
212         checkServiceBinder();
213         if (sService != null) {
214             RestoreSession session = null;
215             try {
216                 IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(),
217                         null);
218                 if (binder != null) {
219                     session = new RestoreSession(mContext, binder);
220                     result = session.restorePackage(mContext.getPackageName(), observer);
221                 }
222             } catch (RemoteException e) {
223                 Log.e(TAG, "restoreSelf() unable to contact service");
224             } finally {
225                 if (session != null) {
226                     session.endRestoreSession();
227                 }
228             }
229         }
230         return result;
231     }
232 
233     // system APIs start here
234 
235     /**
236      * Begin the process of restoring data from backup.  See the
237      * {@link android.app.backup.RestoreSession} class for documentation on that process.
238      * @hide
239      */
240     @SystemApi
beginRestoreSession()241     public RestoreSession beginRestoreSession() {
242         RestoreSession session = null;
243         checkServiceBinder();
244         if (sService != null) {
245             try {
246                 // All packages, current transport
247                 IRestoreSession binder = sService.beginRestoreSession(null, null);
248                 if (binder != null) {
249                     session = new RestoreSession(mContext, binder);
250                 }
251             } catch (RemoteException e) {
252                 Log.e(TAG, "beginRestoreSession() couldn't connect");
253             }
254         }
255         return session;
256     }
257 
258     /**
259      * Enable/disable the backup service entirely.  When disabled, no backup
260      * or restore operations will take place.  Data-changed notifications will
261      * still be observed and collected, however, so that changes made while the
262      * mechanism was disabled will still be backed up properly if it is enabled
263      * at some point in the future.
264      *
265      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
266      *
267      * @hide
268      */
269     @SystemApi
setBackupEnabled(boolean isEnabled)270     public void setBackupEnabled(boolean isEnabled) {
271         checkServiceBinder();
272         if (sService != null) {
273             try {
274                 sService.setBackupEnabled(isEnabled);
275             } catch (RemoteException e) {
276                 Log.e(TAG, "setBackupEnabled() couldn't connect");
277             }
278         }
279     }
280 
281     /**
282      * Report whether the backup mechanism is currently enabled.
283      *
284      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
285      *
286      * @hide
287      */
288     @SystemApi
isBackupEnabled()289     public boolean isBackupEnabled() {
290         checkServiceBinder();
291         if (sService != null) {
292             try {
293                 return sService.isBackupEnabled();
294             } catch (RemoteException e) {
295                 Log.e(TAG, "isBackupEnabled() couldn't connect");
296             }
297         }
298         return false;
299     }
300 
301     /**
302      * Enable/disable data restore at application install time.  When enabled, app
303      * installation will include an attempt to fetch the app's historical data from
304      * the archival restore dataset (if any).  When disabled, no such attempt will
305      * be made.
306      *
307      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
308      *
309      * @hide
310      */
311     @SystemApi
setAutoRestore(boolean isEnabled)312     public void setAutoRestore(boolean isEnabled) {
313         checkServiceBinder();
314         if (sService != null) {
315             try {
316                 sService.setAutoRestore(isEnabled);
317             } catch (RemoteException e) {
318                 Log.e(TAG, "setAutoRestore() couldn't connect");
319             }
320         }
321     }
322 
323     /**
324      * Identify the currently selected transport.  Callers must hold the
325      * android.permission.BACKUP permission to use this method.
326      * @return The name of the currently active backup transport.  In case of
327      *   failure or if no transport is currently active, this method returns {@code null}.
328      *
329      * @hide
330      */
331     @SystemApi
getCurrentTransport()332     public String getCurrentTransport() {
333         checkServiceBinder();
334         if (sService != null) {
335             try {
336                 return sService.getCurrentTransport();
337             } catch (RemoteException e) {
338                 Log.e(TAG, "getCurrentTransport() couldn't connect");
339             }
340         }
341         return null;
342     }
343 
344     /**
345      * Request a list of all available backup transports' names.  Callers must
346      * hold the android.permission.BACKUP permission to use this method.
347      *
348      * @hide
349      */
350     @SystemApi
listAllTransports()351     public String[] listAllTransports() {
352         checkServiceBinder();
353         if (sService != null) {
354             try {
355                 return sService.listAllTransports();
356             } catch (RemoteException e) {
357                 Log.e(TAG, "listAllTransports() couldn't connect");
358             }
359         }
360         return null;
361     }
362 
363     /**
364      * Specify the current backup transport.  Callers must hold the
365      * android.permission.BACKUP permission to use this method.
366      *
367      * @param transport The name of the transport to select.  This should be one
368      *   of the names returned by {@link #listAllTransports()}.
369      * @return The name of the previously selected transport.  If the given transport
370      *   name is not one of the currently available transports, no change is made to
371      *   the current transport setting and the method returns null.
372      *
373      * @hide
374      */
375     @SystemApi
selectBackupTransport(String transport)376     public String selectBackupTransport(String transport) {
377         checkServiceBinder();
378         if (sService != null) {
379             try {
380                 return sService.selectBackupTransport(transport);
381             } catch (RemoteException e) {
382                 Log.e(TAG, "selectBackupTransport() couldn't connect");
383             }
384         }
385         return null;
386     }
387 
388     /**
389      * Schedule an immediate backup attempt for all pending key/value updates.  This
390      * is primarily intended for transports to use when they detect a suitable
391      * opportunity for doing a backup pass.  If there are no pending updates to
392      * be sent, no action will be taken.  Even if some updates are pending, the
393      * transport will still be asked to confirm via the usual requestBackupTime()
394      * method.
395      *
396      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
397      *
398      * @hide
399      */
400     @SystemApi
backupNow()401     public void backupNow() {
402         checkServiceBinder();
403         if (sService != null) {
404             try {
405                 sService.backupNow();
406             } catch (RemoteException e) {
407                 Log.e(TAG, "backupNow() couldn't connect");
408             }
409         }
410     }
411 
412     /**
413      * Ask the framework which dataset, if any, the given package's data would be
414      * restored from if we were to install it right now.
415      *
416      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
417      *
418      * @param packageName The name of the package whose most-suitable dataset we
419      *     wish to look up
420      * @return The dataset token from which a restore should be attempted, or zero if
421      *     no suitable data is available.
422      *
423      * @hide
424      */
425     @SystemApi
getAvailableRestoreToken(String packageName)426     public long getAvailableRestoreToken(String packageName) {
427         checkServiceBinder();
428         if (sService != null) {
429             try {
430                 return sService.getAvailableRestoreToken(packageName);
431             } catch (RemoteException e) {
432                 Log.e(TAG, "getAvailableRestoreToken() couldn't connect");
433             }
434         }
435         return 0;
436     }
437 
438     /**
439      * Ask the framework whether this app is eligible for backup.
440      *
441      * <p>Callers must hold the android.permission.BACKUP permission to use this method.
442      *
443      * @param packageName The name of the package.
444      * @return Whether this app is eligible for backup.
445      *
446      * @hide
447      */
448     @SystemApi
isAppEligibleForBackup(String packageName)449     public boolean isAppEligibleForBackup(String packageName) {
450         checkServiceBinder();
451         if (sService != null) {
452             try {
453                 return sService.isAppEligibleForBackup(packageName);
454             } catch (RemoteException e) {
455                 Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect");
456             }
457         }
458         return false;
459     }
460 
461     /**
462      * Request an immediate backup, providing an observer to which results of the backup operation
463      * will be published. The Android backup system will decide for each package whether it will
464      * be full app data backup or key/value-pair-based backup.
465      *
466      * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all
467      * provided packages using the remote transport.
468      *
469      * @param packages List of package names to backup.
470      * @param observer The {@link BackupObserver} to receive callbacks during the backup
471      * operation. Could be {@code null}.
472      * @return {@link BackupManager#SUCCESS} on success; nonzero on error.
473      * @exception  IllegalArgumentException on null or empty {@code packages} param.
474      *
475      * @hide
476      */
477     @SystemApi
requestBackup(String[] packages, BackupObserver observer)478     public int requestBackup(String[] packages, BackupObserver observer) {
479         checkServiceBinder();
480         if (sService != null) {
481             try {
482                 BackupObserverWrapper observerWrapper = observer == null
483                         ? null
484                         : new BackupObserverWrapper(mContext, observer);
485                 return sService.requestBackup(packages, observerWrapper);
486             } catch (RemoteException e) {
487                 Log.e(TAG, "requestBackup() couldn't connect");
488             }
489         }
490         return -1;
491     }
492 
493     /*
494      * We wrap incoming binder calls with a private class implementation that
495      * redirects them into main-thread actions.  This serializes the backup
496      * progress callbacks nicely within the usual main-thread lifecycle pattern.
497      */
498     @SystemApi
499     private class BackupObserverWrapper extends IBackupObserver.Stub {
500         final Handler mHandler;
501         final BackupObserver mObserver;
502 
503         static final int MSG_UPDATE = 1;
504         static final int MSG_RESULT = 2;
505         static final int MSG_FINISHED = 3;
506 
BackupObserverWrapper(Context context, BackupObserver observer)507         BackupObserverWrapper(Context context, BackupObserver observer) {
508             mHandler = new Handler(context.getMainLooper()) {
509                 @Override
510                 public void handleMessage(Message msg) {
511                     switch (msg.what) {
512                         case MSG_UPDATE:
513                             Pair<String, BackupProgress> obj =
514                                 (Pair<String, BackupProgress>) msg.obj;
515                             mObserver.onUpdate(obj.first, obj.second);
516                             break;
517                         case MSG_RESULT:
518                             mObserver.onResult((String)msg.obj, msg.arg1);
519                             break;
520                         case MSG_FINISHED:
521                             mObserver.backupFinished(msg.arg1);
522                             break;
523                         default:
524                             Log.w(TAG, "Unknown message: " + msg);
525                             break;
526                     }
527                 }
528             };
529             mObserver = observer;
530         }
531 
532         // Binder calls into this object just enqueue on the main-thread handler
533         @Override
onUpdate(String currentPackage, BackupProgress backupProgress)534         public void onUpdate(String currentPackage, BackupProgress backupProgress) {
535             mHandler.sendMessage(
536                 mHandler.obtainMessage(MSG_UPDATE, Pair.create(currentPackage, backupProgress)));
537         }
538 
539         @Override
onResult(String currentPackage, int status)540         public void onResult(String currentPackage, int status) {
541             mHandler.sendMessage(
542                 mHandler.obtainMessage(MSG_RESULT, status, 0, currentPackage));
543         }
544 
545         @Override
backupFinished(int status)546         public void backupFinished(int status) {
547             mHandler.sendMessage(
548                 mHandler.obtainMessage(MSG_FINISHED, status, 0));
549         }
550     }
551 }
552