All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.backup.BackupManagerService Maven / Gradle / Ivy

/*
 * 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 static java.util.Collections.emptySet;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreSession;
import android.app.backup.ISelectBackupTransportCallback;
import android.app.compat.CompatChanges;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.backup.utils.RandomAccessFileUtils;

import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * Definition of the system service that performs backup/restore operations.
 *
 * 

This class is responsible for handling user-aware operations and acts as a delegator, routing * incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the * corresponding backup/restore operation. * *

It also determines whether the backup service is available. It can be disabled in the * following two ways: * *

    *
  • Temporary - call {@link #setBackupServiceActive(int, boolean)}, or *
  • Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true. *
* * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through * privileged callers (currently {@link DevicePolicyManager}). If called on {@link * UserHandle#USER_SYSTEM}, backup is disabled for all users. */ public class BackupManagerService extends IBackupManager.Stub { public static final String TAG = "BackupManagerService"; public static final boolean DEBUG = true; public static final boolean MORE_DEBUG = false; public static final boolean DEBUG_SCHEDULING = true; @VisibleForTesting static final String DUMP_RUNNING_USERS_MESSAGE = "Backup Manager is running for users:"; /** * Name of file that disables the backup service. If this file exists, then backup is disabled * for all users. */ private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress"; /** * Name of file for non-system users that enables the backup service for the user. Backup is * disabled by default in non-system users. */ private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated"; /** * Name of file for non-system users that remembers whether backup was explicitly activated or * deactivated with a call to setBackupServiceActive. */ private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated"; // Product-level suppression of backup/restore. private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable"; private static final String BACKUP_THREAD = "backup"; static BackupManagerService sInstance; static BackupManagerService getInstance() { return Objects.requireNonNull(sInstance); } private final Context mContext; private final UserManager mUserManager; private final boolean mGlobalDisable; // Lock to write backup suppress files. // TODD(b/121198006): remove this object and synchronized all methods on "this". private final Object mStateLock = new Object(); private final Handler mHandler; private final Set mTransportWhitelist; /** Keeps track of all unlocked users registered with this service. Indexed by user id. */ private final SparseArray mUserServices; private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (userId > 0) { // for only non system users mHandler.post(() -> onRemovedNonSystemUser(userId)); } } } }; public BackupManagerService(Context context) { this(context, new SparseArray<>()); } @VisibleForTesting BackupManagerService(Context context, SparseArray userServices) { mContext = context; mGlobalDisable = isBackupDisabled(); HandlerThread handlerThread = new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND); handlerThread.start(); mHandler = new Handler(handlerThread.getLooper()); mUserManager = UserManager.get(context); mUserServices = userServices; Set transportWhitelist = SystemConfig.getInstance().getBackupTransportWhitelist(); mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist; mContext.registerReceiver( mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED)); } // TODO: Remove this when we implement DI by injecting in the construtor. @VisibleForTesting Handler getBackupHandler() { return mHandler; } protected boolean isBackupDisabled() { return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false); } protected int binderGetCallingUserId() { return Binder.getCallingUserHandle().getIdentifier(); } protected int binderGetCallingUid() { return Binder.getCallingUid(); } /** Stored in the system user's directory. */ protected File getSuppressFileForSystemUser() { return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM), BACKUP_SUPPRESS_FILENAME); } /** Stored in the system user's directory and the file is indexed by the user it refers to. */ protected File getRememberActivatedFileForNonSystemUser(int userId) { return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId); } /** Stored in the system user's directory and the file is indexed by the user it refers to. */ protected File getActivatedFileForNonSystemUser(int userId) { return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId); } /** * Remove backup state for non system {@code userId} when the user is removed from the device. * For non system users, backup state is stored in both the user's own dir and the system dir. * When the user is removed, the user's own dir gets removed by the OS. This method ensures that * the part of the user backup state which is in the system dir also gets removed. */ private void onRemovedNonSystemUser(int userId) { Slog.i(TAG, "Removing state for non system user " + userId); File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId); if (!FileUtils.deleteContentsAndDir(dir)) { Slog.w(TAG, "Failed to delete state dir for removed user: " + userId); } } // TODO (b/124359804) move to util method in FileUtils private void createFile(File file) throws IOException { if (file.exists()) { return; } file.getParentFile().mkdirs(); if (!file.createNewFile()) { Slog.w(TAG, "Failed to create file " + file.getPath()); } } // TODO (b/124359804) move to util method in FileUtils private void deleteFile(File file) { if (!file.exists()) { return; } if (!file.delete()) { Slog.w(TAG, "Failed to delete file " + file.getPath()); } } /** * Deactivates the backup service for user {@code userId}. If this is the system user, it * creates a suppress file which disables backup for all users. If this is a non-system user, it * only deactivates backup for that user by deleting its activate file. */ @GuardedBy("mStateLock") private void deactivateBackupForUserLocked(int userId) throws IOException { if (userId == UserHandle.USER_SYSTEM) { createFile(getSuppressFileForSystemUser()); } else { deleteFile(getActivatedFileForNonSystemUser(userId)); } } /** * Enables the backup service for user {@code userId}. If this is the system user, it deletes * the suppress file. If this is a non-system user, it creates the user's activate file. Note, * deleting the suppress file does not automatically enable backup for non-system users, they * need their own activate file in order to participate in the service. */ @GuardedBy("mStateLock") private void activateBackupForUserLocked(int userId) throws IOException { if (userId == UserHandle.USER_SYSTEM) { deleteFile(getSuppressFileForSystemUser()); } else { createFile(getActivatedFileForNonSystemUser(userId)); } } /** * This method should not perform any I/O (e.g. do not call isBackupActivatedForUser), * it's used in multiple places where I/O waits would cause system lock-ups. * @param userId User id for which this operation should be performed. * @return true if the user is ready for backup and false otherwise. */ @Override public boolean isUserReadyForBackup(int userId) { return mUserServices.get(UserHandle.USER_SYSTEM) != null && mUserServices.get(userId) != null; } /** * Backup is activated for the system user if the suppress file does not exist. Backup is * activated for non-system users if the suppress file does not exist AND the user's activated * file exists. */ private boolean isBackupActivatedForUser(int userId) { if (getSuppressFileForSystemUser().exists()) { return false; } return userId == UserHandle.USER_SYSTEM || getActivatedFileForNonSystemUser(userId).exists(); } protected Context getContext() { return mContext; } protected UserManager getUserManager() { return mUserManager; } protected void postToHandler(Runnable runnable) { mHandler.post(runnable); } /** * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked. * Starts the backup service for this user if backup is active for this user. Offloads work onto * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not * essential for device functioning. */ void onUnlockUser(int userId) { postToHandler(() -> startServiceForUser(userId)); } /** * Starts the backup service for user {@code userId} by creating a new instance of {@link * UserBackupManagerService} and registering it with this service. */ @VisibleForTesting void startServiceForUser(int userId) { // We know that the user is unlocked here because it is called from setBackupServiceActive // and unlockUser which have these guarantees. So we can check if the file exists. if (mGlobalDisable) { Slog.i(TAG, "Backup service not supported"); return; } if (!isBackupActivatedForUser(userId)) { Slog.i(TAG, "Backup not activated for user " + userId); return; } if (mUserServices.get(userId) != null) { Slog.i(TAG, "userId " + userId + " already started, so not starting again"); return; } Slog.i(TAG, "Starting service for user: " + userId); UserBackupManagerService userBackupManagerService = UserBackupManagerService.createAndInitializeService( userId, mContext, this, mTransportWhitelist); startServiceForUser(userId, userBackupManagerService); } /** * Starts the backup service for user {@code userId} by registering its instance of {@link * UserBackupManagerService} with this service and setting enabled state. */ void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) { mUserServices.put(userId, userBackupManagerService); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); userBackupManagerService.initializeBackupEnableState(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } /** Stops the backup service for user {@code userId} when the user is stopped. */ @VisibleForTesting protected void stopServiceForUser(int userId) { UserBackupManagerService userBackupManagerService = mUserServices.removeReturnOld(userId); if (userBackupManagerService != null) { userBackupManagerService.tearDownService(); KeyValueBackupJob.cancel(userId, mContext); FullBackupJob.cancel(userId, mContext); } } /** * Returns a list of users currently unlocked that have a {@link UserBackupManagerService} * registered. * * Warning: Do NOT modify returned object as it's used inside. * * TODO: Return a copy or only expose read-only information through other means. */ @VisibleForTesting SparseArray getUserServices() { return mUserServices; } /** * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped. * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low. */ void onStopUser(int userId) { postToHandler( () -> { if (!mGlobalDisable) { Slog.i(TAG, "Stopping service for user: " + userId); stopServiceForUser(userId); } }); } /** Returns {@link UserBackupManagerService} for user {@code userId}. */ @Nullable public UserBackupManagerService getUserService(int userId) { return mUserServices.get(userId); } /** * The system user and managed profiles can only be acted on by callers in the system or root * processes. Other users can be acted on by callers who have both android.permission.BACKUP and * android.permission.INTERACT_ACROSS_USERS_FULL permissions. */ private void enforcePermissionsOnUser(int userId) throws SecurityException { boolean isRestrictedUser = userId == UserHandle.USER_SYSTEM || getUserManager().getUserInfo(userId).isManagedProfile(); if (isRestrictedUser) { int caller = binderGetCallingUid(); if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) { throw new SecurityException("No permission to configure backup activity"); } } else { mContext.enforceCallingOrSelfPermission( Manifest.permission.BACKUP, "No permission to configure backup activity"); mContext.enforceCallingOrSelfPermission( Manifest.permission.INTERACT_ACROSS_USERS_FULL, "No permission to configure backup activity"); } } /** * Only privileged callers should be changing the backup state. Deactivating backup in the * system user also deactivates backup in all users. We are not guaranteed that {@code userId} * is unlocked at this point yet, so handle both cases. */ public void setBackupServiceActive(int userId, boolean makeActive) { enforcePermissionsOnUser(userId); // In Q, backup is OFF by default for non-system users. In the future, we will change that // to ON unless backup was explicitly deactivated with a (permissioned) call to // setBackupServiceActive. // Therefore, remember this for use in the future. Basically the default in the future will // be: rememberFile.exists() ? rememberFile.value() : ON // Note that this has to be done right after the permission checks and before any other // action since we need to remember that a permissioned call was made irrespective of // whether the call changes the state or not. if (userId != UserHandle.USER_SYSTEM) { try { File rememberFile = getRememberActivatedFileForNonSystemUser(userId); createFile(rememberFile); RandomAccessFileUtils.writeBoolean(rememberFile, makeActive); } catch (IOException e) { Slog.e(TAG, "Unable to persist backup service activity", e); } } if (mGlobalDisable) { Slog.i(TAG, "Backup service not supported"); return; } synchronized (mStateLock) { Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active"); if (makeActive) { try { activateBackupForUserLocked(userId); } catch (IOException e) { Slog.e(TAG, "Unable to persist backup service activity"); } // If the user is unlocked, we can start the backup service for it. Otherwise we // will start the service when the user is unlocked as part of its unlock callback. if (getUserManager().isUserUnlocked(userId)) { // Clear calling identity as initialization enforces the system identity but we // can be coming from shell. final long oldId = Binder.clearCallingIdentity(); try { startServiceForUser(userId); } finally { Binder.restoreCallingIdentity(oldId); } } } else { try { //TODO(b/121198006): what if this throws an exception? deactivateBackupForUserLocked(userId); } catch (IOException e) { Slog.e(TAG, "Unable to persist backup service inactivity"); } //TODO(b/121198006): loop through active users that have work profile and // stop them as well. onStopUser(userId); } } } // IBackupManager binder API /** * Querying activity state of backup service. * * @param userId The user in which the activity state of backup service is queried. * @return true if the service is active. */ @Override public boolean isBackupServiceActive(int userId) { int callingUid = Binder.getCallingUid(); if (CompatChanges.isChangeEnabled( BackupManager.IS_BACKUP_SERVICE_ACTIVE_ENFORCE_PERMISSION_IN_SERVICE, callingUid)) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupServiceActive"); } synchronized (mStateLock) { return !mGlobalDisable && isBackupActivatedForUser(userId); } } @Override public void dataChangedForUser(int userId, String packageName) throws RemoteException { if (isUserReadyForBackup(userId)) { dataChanged(userId, packageName); } } @Override public void dataChanged(String packageName) throws RemoteException { dataChangedForUser(binderGetCallingUserId(), packageName); } /** * An app's backup agent calls this method to let the service know that there's new data to * backup for their app {@code packageName}. Only used for apps participating in key-value * backup. */ public void dataChanged(@UserIdInt int userId, String packageName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "dataChanged()"); if (userBackupManagerService != null) { userBackupManagerService.dataChanged(packageName); } } // --------------------------------------------- // TRANSPORT OPERATIONS // --------------------------------------------- @Override public void initializeTransportsForUser( int userId, String[] transportNames, IBackupObserver observer) throws RemoteException { if (isUserReadyForBackup(userId)) { initializeTransports(userId, transportNames, observer); } } /** Run an initialize operation for the given transports {@code transportNames}. */ public void initializeTransports( @UserIdInt int userId, String[] transportNames, IBackupObserver observer) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "initializeTransports()"); if (userBackupManagerService != null) { userBackupManagerService.initializeTransports(transportNames, observer); } } @Override public void clearBackupDataForUser(int userId, String transportName, String packageName) throws RemoteException { if (isUserReadyForBackup(userId)) { clearBackupData(userId, transportName, packageName); } } /** * Clear the given package {@code packageName}'s backup data from the transport {@code * transportName}. */ public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "clearBackupData()"); if (userBackupManagerService != null) { userBackupManagerService.clearBackupData(transportName, packageName); } } @Override public void clearBackupData(String transportName, String packageName) throws RemoteException { clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName); } @Override public void agentConnectedForUser(int userId, String packageName, IBinder agent) throws RemoteException { if (isUserReadyForBackup(userId)) { agentConnected(userId, packageName, agent); } } @Override public void agentConnected(String packageName, IBinder agent) throws RemoteException { agentConnectedForUser(binderGetCallingUserId(), packageName, agent); } /** * Callback: a requested backup agent has been instantiated. This should only be called from the * {@link ActivityManager}. */ public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "agentConnected()"); if (userBackupManagerService != null) { userBackupManagerService.agentConnected(packageName, agentBinder); } } @Override public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException { if (isUserReadyForBackup(userId)) { agentDisconnected(userId, packageName); } } @Override public void agentDisconnected(String packageName) throws RemoteException { agentDisconnectedForUser(binderGetCallingUserId(), packageName); } /** * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be * called from the {@link ActivityManager}. */ public void agentDisconnected(@UserIdInt int userId, String packageName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()"); if (userBackupManagerService != null) { userBackupManagerService.agentDisconnected(packageName); } } @Override public void restoreAtInstallForUser(int userId, String packageName, int token) throws RemoteException { if (isUserReadyForBackup(userId)) { restoreAtInstall(userId, packageName, token); } } @Override public void restoreAtInstall(String packageName, int token) throws RemoteException { restoreAtInstallForUser(binderGetCallingUserId(), packageName, token); } /** * Used to run a restore pass for an application that is being installed. This should only be * called from the {@link PackageManager}. */ public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()"); if (userBackupManagerService != null) { userBackupManagerService.restoreAtInstall(packageName, token); } } @Override public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled) throws RemoteException { if (isUserReadyForBackup(userId)) { setBackupEnabled(userId, isEnabled); } } @Override public void setBackupEnabled(boolean isEnabled) throws RemoteException { setBackupEnabledForUser(binderGetCallingUserId(), isEnabled); } /** Enable/disable the backup service. This is user-configurable via backup settings. */ public void setBackupEnabled(@UserIdInt int userId, boolean enable) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()"); if (userBackupManagerService != null) { userBackupManagerService.setBackupEnabled(enable); } } @Override public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException { if (isUserReadyForBackup(userId)) { setAutoRestore(userId, doAutoRestore); } } @Override public void setAutoRestore(boolean doAutoRestore) throws RemoteException { setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore); } /** Enable/disable automatic restore of app data at install time. */ public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()"); if (userBackupManagerService != null) { userBackupManagerService.setAutoRestore(autoRestore); } } @Override public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException { return isUserReadyForBackup(userId) && isBackupEnabled(userId); } @Override public boolean isBackupEnabled() throws RemoteException { return isBackupEnabledForUser(binderGetCallingUserId()); } /** * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}. */ public boolean isBackupEnabled(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()"); return userBackupManagerService != null && userBackupManagerService.isBackupEnabled(); } /** Sets the backup password used when running adb backup. */ @Override public boolean setBackupPassword(String currentPassword, String newPassword) { int userId = binderGetCallingUserId(); if (!isUserReadyForBackup(userId)) { return false; } UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission( UserHandle.USER_SYSTEM, "setBackupPassword()"); return userBackupManagerService != null && userBackupManagerService.setBackupPassword(currentPassword, newPassword); } /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */ @Override public boolean hasBackupPassword() throws RemoteException { int userId = binderGetCallingUserId(); if (!isUserReadyForBackup(userId)) { return false; } UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission( UserHandle.USER_SYSTEM, "hasBackupPassword()"); return userBackupManagerService != null && userBackupManagerService.hasBackupPassword(); } @Override public void backupNowForUser(@UserIdInt int userId) throws RemoteException { if (isUserReadyForBackup(userId)) { backupNow(userId); } } @Override public void backupNow() throws RemoteException { backupNowForUser(binderGetCallingUserId()); } /** * Run a backup pass immediately for any key-value backup applications that have declared that * they have pending updates. */ public void backupNow(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "backupNow()"); if (userBackupManagerService != null) { userBackupManagerService.backupNow(); } } /** * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the * command line, writing the resulting data stream to the supplied {@code fd}. This method is * synchronous and does not return to the caller until the backup has been completed. It * requires on-screen confirmation by the user. */ @Override public void adbBackup( @UserIdInt int userId, ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem, boolean doCompress, boolean doKeyValue, String[] packageNames) { if (!isUserReadyForBackup(userId)) { return; } UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "adbBackup()"); if (userBackupManagerService != null) { userBackupManagerService.adbBackup( fd, includeApks, includeObbs, includeShared, doWidgets, doAllApps, includeSystem, doCompress, doKeyValue, packageNames); } } @Override public void fullTransportBackupForUser(int userId, String[] packageNames) throws RemoteException { if (isUserReadyForBackup(userId)) { fullTransportBackup(userId, packageNames); } } /** * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'. */ public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()"); if (userBackupManagerService != null) { userBackupManagerService.fullTransportBackup(packageNames); } } /** * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method * is synchronous and does not return to the caller until the restore has been completed. It * requires on-screen confirmation by the user. */ @Override public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) { if (!isUserReadyForBackup(userId)) { return; } UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "adbRestore()"); if (userBackupManagerService != null) { userBackupManagerService.adbRestore(fd); } } @Override public void acknowledgeFullBackupOrRestoreForUser( int userId, int token, boolean allow, String curPassword, String encryptionPassword, IFullBackupRestoreObserver observer) throws RemoteException { if (isUserReadyForBackup(userId)) { acknowledgeAdbBackupOrRestore(userId, token, allow, curPassword, encryptionPassword, observer); } } /** * Confirm that the previously requested adb backup/restore operation can proceed. This is used * to require a user-facing disclosure about the operation. */ public void acknowledgeAdbBackupOrRestore( @UserIdInt int userId, int token, boolean allow, String currentPassword, String encryptionPassword, IFullBackupRestoreObserver observer) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()"); if (userBackupManagerService != null) { userBackupManagerService.acknowledgeAdbBackupOrRestore( token, allow, currentPassword, encryptionPassword, observer); } } @Override public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword, String encryptionPassword, IFullBackupRestoreObserver observer) throws RemoteException { acknowledgeFullBackupOrRestoreForUser( binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer); } @Override public String getCurrentTransportForUser(int userId) throws RemoteException { return (isUserReadyForBackup(userId)) ? getCurrentTransport(userId) : null; } @Override public String getCurrentTransport() throws RemoteException { return getCurrentTransportForUser(binderGetCallingUserId()); } /** Return the name of the currently active transport. */ @Nullable public String getCurrentTransport(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()"); return userBackupManagerService == null ? null : userBackupManagerService.getCurrentTransport(); } /** * Returns the {@link ComponentName} of the host service of the selected transport or * {@code null} if no transport selected or if the transport selected is not registered. */ @Override @Nullable public ComponentName getCurrentTransportComponentForUser(int userId) { return (isUserReadyForBackup(userId)) ? getCurrentTransportComponent(userId) : null; } /** * Returns the {@link ComponentName} of the host service of the selected transport or {@code * null} if no transport selected or if the transport selected is not registered. */ @Nullable public ComponentName getCurrentTransportComponent(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()"); return userBackupManagerService == null ? null : userBackupManagerService.getCurrentTransportComponent(); } @Override public String[] listAllTransportsForUser(int userId) throws RemoteException { return (isUserReadyForBackup(userId)) ? listAllTransports(userId) : null; } /** Report all known, available backup transports by name. */ @Nullable public String[] listAllTransports(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "listAllTransports()"); return userBackupManagerService == null ? null : userBackupManagerService.listAllTransports(); } @Override public String[] listAllTransports() throws RemoteException { return listAllTransportsForUser(binderGetCallingUserId()); } @Override public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException { return (isUserReadyForBackup(userId)) ? listAllTransportComponents(userId) : null; } /** Report all known, available backup transports by {@link ComponentName}. */ @Nullable public ComponentName[] listAllTransportComponents(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()"); return userBackupManagerService == null ? null : userBackupManagerService.listAllTransportComponents(); } @Override public String[] getTransportWhitelist() { int userId = binderGetCallingUserId(); if (!isUserReadyForBackup(userId)) { return null; } // No permission check, intentionally. String[] whitelistedTransports = new String[mTransportWhitelist.size()]; int i = 0; for (ComponentName component : mTransportWhitelist) { whitelistedTransports[i] = component.flattenToShortString(); i++; } return whitelistedTransports; } @Override public void updateTransportAttributesForUser( int userId, ComponentName transportComponent, String name, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, CharSequence dataManagementLabel) { if (isUserReadyForBackup(userId)) { updateTransportAttributes( userId, transportComponent, name, configurationIntent, currentDestinationString, dataManagementIntent, dataManagementLabel); } } /** * Update the attributes of the transport identified by {@code transportComponent}. If the * specified transport has not been bound at least once (for registration), this call will be * ignored. Only the host process of the transport can change its description, otherwise a * {@link SecurityException} will be thrown. * * @param transportComponent The identity of the transport being described. * @param name A {@link String} with the new name for the transport. This is NOT for * identification. MUST NOT be {@code null}. * @param configurationIntent An {@link Intent} that can be passed to {@link * Context#startActivity} in order to launch the transport's configuration UI. It may be * {@code null} if the transport does not offer any user-facing configuration UI. * @param currentDestinationString A {@link String} describing the destination to which the * transport is currently sending data. MUST NOT be {@code null}. * @param dataManagementIntent An {@link Intent} that can be passed to {@link * Context#startActivity} in order to launch the transport's data-management UI. It may be * {@code null} if the transport does not offer any user-facing data management UI. * @param dataManagementLabel A {@link CharSequence} to be used as the label for the transport's * data management affordance. This MUST be {@code null} when dataManagementIntent is {@code * null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}. * @throws SecurityException If the UID of the calling process differs from the package UID of * {@code transportComponent} or if the caller does NOT have BACKUP permission. */ public void updateTransportAttributes( @UserIdInt int userId, ComponentName transportComponent, String name, @Nullable Intent configurationIntent, String currentDestinationString, @Nullable Intent dataManagementIntent, CharSequence dataManagementLabel) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()"); if (userBackupManagerService != null) { userBackupManagerService.updateTransportAttributes( transportComponent, name, configurationIntent, currentDestinationString, dataManagementIntent, dataManagementLabel); } } @Override public String selectBackupTransportForUser(int userId, String transport) throws RemoteException { return (isUserReadyForBackup(userId)) ? selectBackupTransport(userId, transport) : null; } @Override public String selectBackupTransport(String transport) throws RemoteException { return selectBackupTransportForUser(binderGetCallingUserId(), transport); } /** * Selects transport {@code transportName} and returns the previously selected transport. * * @deprecated Use {@link #selectBackupTransportAsync(ComponentName, * ISelectBackupTransportCallback)} instead. */ @Deprecated @Nullable public String selectBackupTransport(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()"); return userBackupManagerService == null ? null : userBackupManagerService.selectBackupTransport(transportName); } @Override public void selectBackupTransportAsyncForUser(int userId, ComponentName transport, ISelectBackupTransportCallback listener) throws RemoteException { if (isUserReadyForBackup(userId)) { selectBackupTransportAsync(userId, transport, listener); } else { if (listener != null) { try { listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED); } catch (RemoteException ex) { // ignore } } } } /** * Selects transport {@code transportComponent} asynchronously and notifies {@code listener} * with the result upon completion. */ public void selectBackupTransportAsync( @UserIdInt int userId, ComponentName transportComponent, ISelectBackupTransportCallback listener) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()"); if (userBackupManagerService != null) { userBackupManagerService.selectBackupTransportAsync(transportComponent, listener); } } @Override public Intent getConfigurationIntentForUser(int userId, String transport) throws RemoteException { return isUserReadyForBackup(userId) ? getConfigurationIntent(userId, transport) : null; } @Override public Intent getConfigurationIntent(String transport) throws RemoteException { return getConfigurationIntentForUser(binderGetCallingUserId(), transport); } /** * Supply the configuration intent for the given transport. If the name is not one of the * available transports, or if the transport does not supply any configuration UI, the method * returns {@code null}. */ @Nullable public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()"); return userBackupManagerService == null ? null : userBackupManagerService.getConfigurationIntent(transportName); } @Override public String getDestinationStringForUser(int userId, String transport) throws RemoteException { return isUserReadyForBackup(userId) ? getDestinationString(userId, transport) : null; } @Override public String getDestinationString(String transport) throws RemoteException { return getDestinationStringForUser(binderGetCallingUserId(), transport); } /** * Supply the current destination string for the given transport. If the name is not one of the * registered transports the method will return null. * *

This string is used VERBATIM as the summary text of the relevant Settings item. * * @param transportName The name of the registered transport. * @return The current destination string or null if the transport is not registered. */ @Nullable public String getDestinationString(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "getDestinationString()"); return userBackupManagerService == null ? null : userBackupManagerService.getDestinationString(transportName); } @Override public Intent getDataManagementIntentForUser(int userId, String transport) throws RemoteException { return isUserReadyForBackup(userId) ? getDataManagementIntent(userId, transport) : null; } @Override public Intent getDataManagementIntent(String transport) throws RemoteException { return getDataManagementIntentForUser(binderGetCallingUserId(), transport); } /** Supply the manage-data intent for the given transport. */ @Nullable public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()"); return userBackupManagerService == null ? null : userBackupManagerService.getDataManagementIntent(transportName); } @Override public CharSequence getDataManagementLabelForUser(int userId, String transport) throws RemoteException { return isUserReadyForBackup(userId) ? getDataManagementLabel(userId, transport) : null; } /** * Supply the menu label for affordances that fire the manage-data intent for the given * transport. */ @Nullable public CharSequence getDataManagementLabel(@UserIdInt int userId, String transportName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()"); return userBackupManagerService == null ? null : userBackupManagerService.getDataManagementLabel(transportName); } @Override public IRestoreSession beginRestoreSessionForUser( int userId, String packageName, String transportID) throws RemoteException { return isUserReadyForBackup(userId) ? beginRestoreSession(userId, packageName, transportID) : null; } /** * Begin a restore for the specified package {@code packageName} using the specified transport * {@code transportName}. */ @Nullable public IRestoreSession beginRestoreSession( @UserIdInt int userId, String packageName, String transportName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()"); return userBackupManagerService == null ? null : userBackupManagerService.beginRestoreSession(packageName, transportName); } @Override public void opCompleteForUser(int userId, int token, long result) throws RemoteException { if (isUserReadyForBackup(userId)) { opComplete(userId, token, result); } } @Override public void opComplete(int token, long result) throws RemoteException { opCompleteForUser(binderGetCallingUserId(), token, result); } /** * Used by a currently-active backup agent to notify the service that it has completed its given * outstanding asynchronous backup/restore operation. */ public void opComplete(@UserIdInt int userId, int token, long result) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "opComplete()"); if (userBackupManagerService != null) { userBackupManagerService.opComplete(token, result); } } @Override public long getAvailableRestoreTokenForUser(int userId, String packageName) { return isUserReadyForBackup(userId) ? getAvailableRestoreToken(userId, packageName) : 0; } /** * Get the restore-set token for the best-available restore set for this {@code packageName}: * the active set if possible, else the ancestral one. Returns zero if none available. */ public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()"); return userBackupManagerService == null ? 0 : userBackupManagerService.getAvailableRestoreToken(packageName); } @Override public boolean isAppEligibleForBackupForUser(int userId, String packageName) { return isUserReadyForBackup(userId) && isAppEligibleForBackup(userId, packageName); } /** Checks if the given package {@code packageName} is eligible for backup. */ public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()"); return userBackupManagerService != null && userBackupManagerService.isAppEligibleForBackup(packageName); } @Override public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) { return isUserReadyForBackup(userId) ? filterAppsEligibleForBackup(userId, packages) : null; } /** * Returns from the inputted packages {@code packages}, the ones that are eligible for backup. */ @Nullable public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()"); return userBackupManagerService == null ? null : userBackupManagerService.filterAppsEligibleForBackup(packages); } @Override public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) throws RemoteException { if (!isUserReadyForBackup(userId)) { return BackupManager.ERROR_BACKUP_NOT_ALLOWED; } return requestBackup(userId, packages, observer, monitor, flags); } @Override public int requestBackup(String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) throws RemoteException { return requestBackup(binderGetCallingUserId(), packages, observer, monitor, flags); } /** * Requests a backup for the inputted {@code packages} with a specified callback {@link * IBackupManagerMonitor} for receiving events during the operation. */ public int requestBackup( @UserIdInt int userId, String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "requestBackup()"); return userBackupManagerService == null ? BackupManager.ERROR_BACKUP_NOT_ALLOWED : userBackupManagerService.requestBackup(packages, observer, monitor, flags); } @Override public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException { if (isUserReadyForBackup(userId)) { cancelBackups(userId); } } @Override public void cancelBackups() throws RemoteException { cancelBackupsForUser(binderGetCallingUserId()); } /** Cancel all running backup operations. */ public void cancelBackups(@UserIdInt int userId) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "cancelBackups()"); if (userBackupManagerService != null) { userBackupManagerService.cancelBackups(); } } /** * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the * serial number of its ancestral work profile or null if there is no {@link * UserBackupManagerService} associated with that user. * *

The ancestral work profile is set by {@link #setAncestralSerialNumber(long)} * and it corresponds to the profile that was used to restore to the callers profile. */ @Override @Nullable public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) { if (mGlobalDisable) { return null; } int callingUserId = Binder.getCallingUserHandle().getIdentifier(); final int[] userIds; final long oldId = Binder.clearCallingIdentity(); try { userIds = getUserManager().getProfileIds(callingUserId, false); } finally { Binder.restoreCallingIdentity(oldId); } for (int userId : userIds) { UserBackupManagerService userBackupManagerService = mUserServices.get(userId); if (userBackupManagerService != null) { if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) { return UserHandle.of(userId); } } } return null; } /** * Sets the ancestral work profile for the calling user. * *

The ancestral work profile corresponds to the profile that was used to restore to the * callers profile. */ @Override public void setAncestralSerialNumber(long ancestralSerialNumber) { if (mGlobalDisable) { return; } UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission( Binder.getCallingUserHandle().getIdentifier(), "setAncestralSerialNumber()"); if (userBackupManagerService != null) { userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber); } } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) { return; } dumpWithoutCheckingPermission(fd, pw, args); } @VisibleForTesting void dumpWithoutCheckingPermission(FileDescriptor fd, PrintWriter pw, String[] args) { int userId = binderGetCallingUserId(); if (!isUserReadyForBackup(userId)) { pw.println("Inactive"); return; } if (args != null) { for (String arg : args) { if ("-h".equals(arg)) { pw.println("'dumpsys backup' optional arguments:"); pw.println(" -h : this help text"); pw.println(" a[gents] : dump information about defined backup agents"); pw.println(" transportclients : dump information about transport clients"); pw.println(" transportstats : dump transport statts"); pw.println(" users : dump the list of users for which backup service " + "is running"); return; } else if ("users".equals(arg.toLowerCase())) { pw.print(DUMP_RUNNING_USERS_MESSAGE); for (int i = 0; i < mUserServices.size(); i++) { pw.print(" " + mUserServices.keyAt(i)); } pw.println(); return; } } } for (int i = 0; i < mUserServices.size(); i++) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(mUserServices.keyAt(i), "dump()"); if (userBackupManagerService != null) { userBackupManagerService.dump(fd, pw, args); } } } /** * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we * use is to perform one app backup per scheduled job execution, and to reschedule the job with * zero latency as long as conditions remain right and we still have work to do. * * @return Whether ongoing work will continue. The return value here will be passed along as the * return value to the callback {@link JobService#onStartJob(JobParameters)}. */ public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) { if (!isUserReadyForBackup(userId)) { return false; } UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()"); return userBackupManagerService != null && userBackupManagerService.beginFullBackup(scheduledJob); } /** * Used by the {@link JobScheduler} to end the current full backup task when conditions are no * longer met for running the full backup job. */ public void endFullBackup(@UserIdInt int userId) { if (!isUserReadyForBackup(userId)) { return; } UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "endFullBackup()"); if (userBackupManagerService != null) { userBackupManagerService.endFullBackup(); } } /** * Excludes keys from KV restore for a given package. The corresponding data will be excluded * from the data set available the backup agent during restore. However, final list of keys * that have been excluded will be passed to the agent to make it aware of the exclusions. */ public void excludeKeysFromRestore(String packageName, List keys) { int userId = Binder.getCallingUserHandle().getIdentifier(); if (!isUserReadyForBackup(userId)) { Slog.w(TAG, "Returning from excludeKeysFromRestore as backup for user" + userId + " is not initialized yet"); return; } UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "excludeKeysFromRestore()"); if (userBackupManagerService != null) { userBackupManagerService.excludeKeysFromRestore(packageName, keys); } } /** * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}. * If the user is not registered with the service (either the user is locked or not eligible for * the backup service) then return {@code null}. * * @param userId The id of the user to retrieve its instance of {@link * UserBackupManagerService}. * @param caller A {@link String} identifying the caller for logging purposes. * @throws SecurityException if {@code userId} is different from the calling user id and the * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission. */ @Nullable @VisibleForTesting UserBackupManagerService getServiceForUserIfCallerHasPermission( @UserIdInt int userId, String caller) { enforceCallingPermissionOnUserId(userId, caller); UserBackupManagerService userBackupManagerService = mUserServices.get(userId); if (userBackupManagerService == null) { Slog.w(TAG, "Called " + caller + " for unknown user: " + userId); } return userBackupManagerService; } /** * If {@code userId} is different from the calling user id, then the caller must hold the * android.permission.INTERACT_ACROSS_USERS_FULL permission. * * @param userId User id on which the backup operation is being requested. * @param message A message to include in the exception if it is thrown. */ void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) { if (Binder.getCallingUserHandle().getIdentifier() != userId) { mContext.enforceCallingOrSelfPermission( Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); } } /** Implementation to receive lifecycle event callbacks for system services. */ public static class Lifecycle extends SystemService { public Lifecycle(Context context) { this(context, new BackupManagerService(context)); } @VisibleForTesting Lifecycle(Context context, BackupManagerService backupManagerService) { super(context); sInstance = backupManagerService; } @Override public void onStart() { publishService(Context.BACKUP_SERVICE, BackupManagerService.sInstance); } @Override public void onUserUnlocking(@NonNull TargetUser user) { sInstance.onUnlockUser(user.getUserIdentifier()); } @Override public void onUserStopping(@NonNull TargetUser user) { sInstance.onStopUser(user.getUserIdentifier()); } @VisibleForTesting void publishService(String name, IBinder service) { publishBinderService(name, service); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy