summaryrefslogtreecommitdiffstats
path: root/services/backup
diff options
context:
space:
mode:
authorChristopher Tate <ctate@google.com>2014-10-30 13:44:27 -0700
committerChristopher Tate <ctate@android.com>2014-11-07 18:40:47 +0000
commitbbe23b31dcd0eec8af5b5198970de7ff2a9ef79a (patch)
treee0f6458989ba16677e19f930fcfd128af6d11d3f /services/backup
parent4b19b7aaff1d1ff972ebe68101c2107454bbe5de (diff)
downloadframeworks_base-bbe23b31dcd0eec8af5b5198970de7ff2a9ef79a.zip
frameworks_base-bbe23b31dcd0eec8af5b5198970de7ff2a9ef79a.tar.gz
frameworks_base-bbe23b31dcd0eec8af5b5198970de7ff2a9ef79a.tar.bz2
Enable runtime turndown of backup/restore services
The heavy implementation of the backup manager service is now sitting behind a lightweight trampoline that actually provides the binder call interface. The indirection allows us now to tear down the implementation on the fly without breaking callers who have cached binder references to the backup services: these callers will simply see their future invocations failing benignly. In addition there is now an API for suitably privileged callers such as device policy management to effect this turndown. Finally, there is now a static system property, "ro.backup.disable", that a product can use to outright remove backup/restore operation from the system's operation. The public APIs will continue to be safely usable on such products but no data will be moved to or from the device. Bug 17367491 Change-Id: I8108e386ef3b5c967938fae483366d6978fe4e04
Diffstat (limited to 'services/backup')
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java37
-rw-r--r--services/backup/java/com/android/server/backup/FullBackupJob.java4
-rw-r--r--services/backup/java/com/android/server/backup/Trampoline.java327
3 files changed, 348 insertions, 20 deletions
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index fea1a7a..6c2681b 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -153,7 +153,7 @@ import javax.crypto.spec.SecretKeySpec;
import libcore.io.IoUtils;
-public class BackupManagerService extends IBackupManager.Stub {
+public class BackupManagerService {
private static final String TAG = "BackupManagerService";
private static final boolean DEBUG = true;
@@ -322,8 +322,12 @@ public class BackupManagerService extends IBackupManager.Stub {
// Watch the device provisioning operation during setup
ContentObserver mProvisionedObserver;
- static BackupManagerService sInstance;
- static BackupManagerService getInstance() {
+ // The published binder is actually to a singleton trampoline object that calls
+ // through to the proper code. This indirection lets us turn down the heavy
+ // implementation object on the fly without disturbing binders that have been
+ // cached elsewhere in the system.
+ static Trampoline sInstance;
+ static Trampoline getInstance() {
// Always constructed during system bringup, so no need to lazy-init
return sInstance;
}
@@ -332,7 +336,7 @@ public class BackupManagerService extends IBackupManager.Stub {
public Lifecycle(Context context) {
super(context);
- sInstance = new BackupManagerService(context);
+ sInstance = new Trampoline(context);
}
@Override
@@ -342,11 +346,17 @@ public class BackupManagerService extends IBackupManager.Stub {
@Override
public void onBootPhase(int phase) {
- if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ sInstance.initialize(UserHandle.USER_OWNER);
+ } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
ContentResolver r = sInstance.mContext.getContentResolver();
boolean areEnabled = Settings.Secure.getInt(r,
Settings.Secure.BACKUP_ENABLED, 0) != 0;
- sInstance.setBackupEnabled(areEnabled);
+ try {
+ sInstance.setBackupEnabled(areEnabled);
+ } catch (RemoteException e) {
+ // can't happen; it's a local object
+ }
}
}
}
@@ -934,7 +944,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// ----- Main service implementation -----
- public BackupManagerService(Context context) {
+ public BackupManagerService(Context context, Trampoline parent) {
mContext = context;
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
@@ -944,7 +954,7 @@ public class BackupManagerService extends IBackupManager.Stub {
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
- mBackupManagerBinder = asInterface(asBinder());
+ mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
// spin up the backup/restore handler thread
mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
@@ -1451,7 +1461,6 @@ public class BackupManagerService extends IBackupManager.Stub {
return false;
}
- @Override
public boolean setBackupPassword(String currentPw, String newPw) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupPassword");
@@ -1532,7 +1541,6 @@ public class BackupManagerService extends IBackupManager.Stub {
return false;
}
- @Override
public boolean hasBackupPassword() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"hasBackupPassword");
@@ -8145,7 +8153,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
//
// This is the variant used by 'adb backup'; it requires on-screen confirmation
// by the user because it can be used to offload data over untrusted USB.
- @Override
public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
boolean includeObbs, boolean includeShared, boolean doWidgets,
boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
@@ -8217,7 +8224,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
}
- @Override
public void fullTransportBackup(String[] pkgNames) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
"fullTransportBackup");
@@ -8247,7 +8253,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
}
- @Override
public void fullRestore(ParcelFileDescriptor fd) {
mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullRestore");
@@ -8343,7 +8348,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
// Confirm that the previously-requested full backup/restore operation can proceed. This
// is used to require a user-facing disclosure about the operation.
- @Override
public void acknowledgeFullBackupOrRestore(int token, boolean allow,
String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
@@ -8391,8 +8395,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
}
- // Enable/disable the backup service
- @Override
+ // Enable/disable backups
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupEnabled");
@@ -8798,7 +8801,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
// Note that a currently-active backup agent has notified us that it has
// completed the given outstanding asynchronous backup/restore operation.
- @Override
public void opComplete(int token) {
if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
Operation op = null;
@@ -9147,7 +9149,6 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
}
- @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 601f15e..7ad7657c 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -59,7 +59,7 @@ public class FullBackupJob extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
mParams = params;
- BackupManagerService service = BackupManagerService.getInstance();
+ Trampoline service = BackupManagerService.getInstance();
return service.beginFullBackup(this);
}
@@ -67,7 +67,7 @@ public class FullBackupJob extends JobService {
public boolean onStopJob(JobParameters params) {
if (mParams != null) {
mParams = null;
- BackupManagerService service = BackupManagerService.getInstance();
+ Trampoline service = BackupManagerService.getInstance();
service.endFullBackup();
}
return false;
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
new file mode 100644
index 0000000..5d2187f
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.app.backup.IBackupManager;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.IRestoreSession;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class Trampoline extends IBackupManager.Stub {
+ static final String TAG = "BackupManagerService";
+ static final boolean DEBUG_TRAMPOLINE = false;
+
+ // When this file is present, the backup service is inactive
+ static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
+
+ // Product-level suppression of backup/restore
+ static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
+
+ final Context mContext;
+ final File mSuppressFile; // existence testing & creating synchronized on 'this'
+ final boolean mGlobalDisable;
+ volatile BackupManagerService mService;
+
+ public Trampoline(Context context) {
+ mContext = context;
+ File dir = new File(Environment.getSecureDataDirectory(), "backup");
+ dir.mkdirs();
+ mSuppressFile = new File(dir, BACKUP_SUPPRESS_FILENAME);
+ mGlobalDisable = SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
+ }
+
+ // internal control API
+ public void initialize(final int whichUser) {
+ // Note that only the owner user is currently involved in backup/restore
+ if (whichUser == UserHandle.USER_OWNER) {
+ // Does this product support backup/restore at all?
+ if (mGlobalDisable) {
+ Slog.i(TAG, "Backup/restore not supported");
+ return;
+ }
+
+ synchronized (this) {
+ if (!mSuppressFile.exists()) {
+ mService = new BackupManagerService(mContext, this);
+ } else {
+ Slog.i(TAG, "Backup inactive in user " + whichUser);
+ }
+ }
+ }
+ }
+
+ public void setBackupServiceActive(final int userHandle, boolean makeActive) {
+ // Only the DPM should be changing the active state of backup
+ final int caller = Binder.getCallingUid();
+ if (caller != Process.SYSTEM_UID
+ && caller != Process.ROOT_UID) {
+ throw new SecurityException("No permission to configure backup activity");
+ }
+
+ if (mGlobalDisable) {
+ Slog.i(TAG, "Backup/restore not supported");
+ return;
+ }
+
+ if (userHandle == UserHandle.USER_OWNER) {
+ synchronized (this) {
+ if (makeActive != (mService != null)) {
+ Slog.i(TAG, "Making backup "
+ + (makeActive ? "" : "in") + "active in user " + userHandle);
+ if (makeActive) {
+ mService = new BackupManagerService(mContext, this);
+ mSuppressFile.delete();
+ } else {
+ mService = null;
+ try {
+ mSuppressFile.createNewFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to persist backup service inactivity");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // IBackupManager binder API
+ @Override
+ public void dataChanged(String packageName) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.dataChanged(packageName);
+ }
+ }
+
+ @Override
+ public void clearBackupData(String transportName, String packageName)
+ throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.clearBackupData(transportName, packageName);
+ }
+ }
+
+ @Override
+ public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.agentConnected(packageName, agent);
+ }
+ }
+
+ @Override
+ public void agentDisconnected(String packageName) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.agentDisconnected(packageName);
+ }
+ }
+
+ @Override
+ public void restoreAtInstall(String packageName, int token) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.restoreAtInstall(packageName, token);
+ }
+ }
+
+ @Override
+ public void setBackupEnabled(boolean isEnabled) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.setBackupEnabled(isEnabled);
+ }
+ }
+
+ @Override
+ public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.setAutoRestore(doAutoRestore);
+ }
+ }
+
+ @Override
+ public void setBackupProvisioned(boolean isProvisioned) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.setBackupProvisioned(isProvisioned);
+ }
+ }
+
+ @Override
+ public boolean isBackupEnabled() throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.isBackupEnabled() : false;
+ }
+
+ @Override
+ public boolean setBackupPassword(String currentPw, String newPw) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.setBackupPassword(currentPw, newPw) : false;
+ }
+
+ @Override
+ public boolean hasBackupPassword() throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.hasBackupPassword() : false;
+ }
+
+ @Override
+ public void backupNow() throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.backupNow();
+ }
+ }
+
+ @Override
+ public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
+ boolean includeShared, boolean doWidgets, boolean allApps,
+ boolean allIncludesSystem, boolean doCompress, String[] packageNames)
+ throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.fullBackup(fd, includeApks, includeObbs, includeShared, doWidgets,
+ allApps, allIncludesSystem, doCompress, packageNames);
+ }
+ }
+
+ @Override
+ public void fullTransportBackup(String[] packageNames) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.fullTransportBackup(packageNames);
+ }
+ }
+
+ @Override
+ public void fullRestore(ParcelFileDescriptor fd) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.fullRestore(fd);
+ }
+ }
+
+ @Override
+ public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
+ String encryptionPassword, IFullBackupRestoreObserver observer)
+ throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.acknowledgeFullBackupOrRestore(token, allow,
+ curPassword, encryptionPassword, observer);
+ }
+ }
+
+ @Override
+ public String getCurrentTransport() throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getCurrentTransport() : null;
+ }
+
+ @Override
+ public String[] listAllTransports() throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.listAllTransports() : null;
+ }
+
+ @Override
+ public String selectBackupTransport(String transport) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.selectBackupTransport(transport) : null;
+ }
+
+ @Override
+ public Intent getConfigurationIntent(String transport) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getConfigurationIntent(transport) : null;
+ }
+
+ @Override
+ public String getDestinationString(String transport) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getDestinationString(transport) : null;
+ }
+
+ @Override
+ public Intent getDataManagementIntent(String transport) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getDataManagementIntent(transport) : null;
+ }
+
+ @Override
+ public String getDataManagementLabel(String transport) throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.getDataManagementLabel(transport) : null;
+ }
+
+ @Override
+ public IRestoreSession beginRestoreSession(String packageName, String transportID)
+ throws RemoteException {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null;
+ }
+
+ @Override
+ public void opComplete(int token) throws RemoteException {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.opComplete(token);
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.dump(fd, pw, args);
+ } else {
+ pw.println("Inactive");
+ }
+ }
+
+ // Full backup/restore entry points - non-Binder; called directly
+ // by the full-backup scheduled job
+ /* package */ boolean beginFullBackup(FullBackupJob scheduledJob) {
+ BackupManagerService svc = mService;
+ return (svc != null) ? svc.beginFullBackup(scheduledJob) : false;
+ }
+
+ /* package */ void endFullBackup() {
+ BackupManagerService svc = mService;
+ if (svc != null) {
+ svc.endFullBackup();
+ }
+ }
+}