[Multi-user] Create setting for multi-user backup service support
Whether the backup service supports multi-user is now configured in a Global setting: backup_multi_user_enabled This allows us to develop multi-user support hidden behind a flag. In a future CL, we'll also gate the types of users we support. Also create basic infrastructure for starting the service for a newly unlocked user (currently a no-op). Bug: 120212806 Test: 1) atest TrampolineTest 2) adb shell settings put global backup_multi_user_enabled 0; unlock system user -> verify service started; unlock user 10 -> verify service not started; 3) adb shell settings put global backup_multi_user_enabled 1; unlock system user -> verify service started; unlock user 10 -> verify service started; Change-Id: I048e017cfa6148097cebe2eb2916d1b53c53d3b0
This commit is contained in:
@@ -13794,6 +13794,14 @@ public final class Settings {
|
||||
public static final String BACKUP_AGENT_TIMEOUT_PARAMETERS =
|
||||
"backup_agent_timeout_parameters";
|
||||
|
||||
/**
|
||||
* Whether the backup system service supports multiple users (0 = disabled, 1 = enabled). If
|
||||
* disabled, the service will only be active for the system user.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String BACKUP_MULTI_USER_ENABLED = "backup_multi_user_enabled";
|
||||
|
||||
/**
|
||||
* Blacklist of GNSS satellites.
|
||||
*
|
||||
|
||||
@@ -109,7 +109,15 @@ message GlobalSettingsProto {
|
||||
}
|
||||
optional Autofill autofill = 140;
|
||||
|
||||
optional SettingProto backup_agent_timeout_parameters = 18;
|
||||
reserved 18; // Used to be backup_agent_timeout_parameters
|
||||
|
||||
message Backup {
|
||||
option (android.msg_privacy).dest = DEST_EXPLICIT;
|
||||
|
||||
optional SettingProto backup_agent_timeout_parameters = 1;
|
||||
optional SettingProto backup_multi_user_enabled = 2;
|
||||
}
|
||||
optional Backup backup = 146;
|
||||
|
||||
message Battery {
|
||||
option (android.msg_privacy).dest = DEST_EXPLICIT;
|
||||
@@ -1007,5 +1015,5 @@ message GlobalSettingsProto {
|
||||
|
||||
// Please insert fields in alphabetical order and group them into messages
|
||||
// if possible (to avoid reaching the method limit).
|
||||
// Next tag = 146;
|
||||
// Next tag = 147;
|
||||
}
|
||||
|
||||
@@ -541,7 +541,8 @@ public class SettingsBackupTest {
|
||||
Settings.Global.OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION,
|
||||
Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
|
||||
Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
|
||||
Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS);
|
||||
Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
|
||||
Settings.Global.BACKUP_MULTI_USER_ENABLED);
|
||||
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
|
||||
newHashSet(
|
||||
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
|
||||
|
||||
@@ -208,9 +208,14 @@ class SettingsProtoDumpUtil {
|
||||
GlobalSettingsProto.Autofill.MAX_VISIBLE_DATASETS);
|
||||
p.end(autofillToken);
|
||||
|
||||
final long backupToken = p.start(GlobalSettingsProto.BACKUP);
|
||||
dumpSetting(s, p,
|
||||
Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
|
||||
GlobalSettingsProto.BACKUP_AGENT_TIMEOUT_PARAMETERS);
|
||||
GlobalSettingsProto.Backup.BACKUP_AGENT_TIMEOUT_PARAMETERS);
|
||||
dumpSetting(s, p,
|
||||
Settings.Global.BACKUP_MULTI_USER_ENABLED,
|
||||
GlobalSettingsProto.Backup.BACKUP_MULTI_USER_ENABLED);
|
||||
p.end(backupToken);
|
||||
|
||||
final long batteryToken = p.start(GlobalSettingsProto.BATTERY);
|
||||
dumpSetting(s, p,
|
||||
|
||||
@@ -184,6 +184,15 @@ public class BackupManagerService {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the backup service for user {@code userId} by creating a new instance of {@link
|
||||
* UserBackupManagerService} and registering it with this service.
|
||||
*/
|
||||
// TODO(b/120212806): Add UserBackupManagerService initialization logic.
|
||||
void startServiceForUser(int userId) {
|
||||
// Intentionally empty.
|
||||
}
|
||||
|
||||
/*
|
||||
* The following methods are implementations of IBackupManager methods called from Trampoline.
|
||||
* They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
|
||||
@@ -626,7 +635,9 @@ public class BackupManagerService {
|
||||
@Override
|
||||
public void onUnlockUser(int userId) {
|
||||
if (userId == UserHandle.USER_SYSTEM) {
|
||||
sInstance.unlockSystemUser();
|
||||
sInstance.initializeServiceAndUnlockSystemUser();
|
||||
} else {
|
||||
sInstance.startServiceForUser(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -81,6 +82,12 @@ public class Trampoline extends IBackupManager.Stub {
|
||||
// Product-level suppression of backup/restore.
|
||||
private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
|
||||
|
||||
private static final String BACKUP_THREAD = "backup";
|
||||
|
||||
/** Values for setting {@link Settings.Global#BACKUP_MULTI_USER_ENABLED} */
|
||||
private static final int MULTI_USER_DISABLED = 0;
|
||||
private static final int MULTI_USER_ENABLED = 1;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
@GuardedBy("mStateLock")
|
||||
@@ -91,18 +98,31 @@ public class Trampoline extends IBackupManager.Stub {
|
||||
|
||||
private volatile BackupManagerService mService;
|
||||
private HandlerThread mHandlerThread;
|
||||
private Handler mHandler;
|
||||
|
||||
public Trampoline(Context context) {
|
||||
mContext = context;
|
||||
mGlobalDisable = isBackupDisabled();
|
||||
mSuppressFile = getSuppressFile();
|
||||
mSuppressFile.getParentFile().mkdirs();
|
||||
|
||||
mHandlerThread = new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
|
||||
mHandlerThread.start();
|
||||
mHandler = new Handler(mHandlerThread.getLooper());
|
||||
}
|
||||
|
||||
protected boolean isBackupDisabled() {
|
||||
return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
|
||||
}
|
||||
|
||||
protected boolean isMultiUserEnabled() {
|
||||
return Settings.Global.getInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Global.BACKUP_MULTI_USER_ENABLED,
|
||||
MULTI_USER_DISABLED)
|
||||
== MULTI_USER_ENABLED;
|
||||
}
|
||||
|
||||
protected int binderGetCallingUid() {
|
||||
return Binder.getCallingUid();
|
||||
}
|
||||
@@ -147,15 +167,9 @@ public class Trampoline extends IBackupManager.Stub {
|
||||
/**
|
||||
* Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts
|
||||
* to initialize {@link BackupManagerService} and set backup state for the system user.
|
||||
*
|
||||
* @see BackupManagerService#unlockSystemUser()
|
||||
*/
|
||||
void unlockSystemUser() {
|
||||
mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
|
||||
mHandlerThread.start();
|
||||
|
||||
Handler h = new Handler(mHandlerThread.getLooper());
|
||||
h.post(
|
||||
void initializeServiceAndUnlockSystemUser() {
|
||||
mHandler.post(
|
||||
() -> {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
|
||||
initializeService(UserHandle.USER_SYSTEM);
|
||||
@@ -169,6 +183,29 @@ public class Trampoline extends IBackupManager.Stub {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from {@link BackupManagerService.Lifecycle} when a non-system user {@code userId} is
|
||||
* unlocked. Starts the backup service for this user if the service supports multi-user.
|
||||
* Offloads work onto the handler thread {@link #mHandlerThread} to keep unlock time low.
|
||||
*/
|
||||
// TODO(b/120212806): Consolidate service start for system and non-system users when system
|
||||
// user-only logic is removed.
|
||||
void startServiceForUser(int userId) {
|
||||
if (!isMultiUserEnabled()) {
|
||||
Slog.i(TAG, "Multi-user disabled, cannot start service for user: " + userId);
|
||||
return;
|
||||
}
|
||||
|
||||
mHandler.post(
|
||||
() -> {
|
||||
BackupManagerService service = mService;
|
||||
if (service != null) {
|
||||
Slog.i(TAG, "Starting service for user: " + userId);
|
||||
service.startServiceForUser(userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Only privileged callers should be changing the backup state. This method only acts on {@link
|
||||
* UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
|
||||
|
||||
@@ -23,6 +23,7 @@ import static junit.framework.Assert.assertNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.Assert.fail;
|
||||
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
@@ -43,10 +44,14 @@ import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.provider.Settings;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.util.test.FakeSettingsProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -97,6 +102,7 @@ public class TrampolineTest {
|
||||
private FileDescriptor mFileDescriptorStub = new FileDescriptor();
|
||||
|
||||
private TrampolineTestable mTrampoline;
|
||||
private MockContentResolver mContentResolver;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -110,6 +116,10 @@ public class TrampolineTest {
|
||||
when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
|
||||
|
||||
mTrampoline = new TrampolineTestable(mContextMock);
|
||||
|
||||
mContentResolver = new MockContentResolver();
|
||||
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
|
||||
when(mContextMock.getContentResolver()).thenReturn(mContentResolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -117,6 +127,24 @@ public class TrampolineTest {
|
||||
verify(mSuppressFileParentMock).mkdirs();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startServiceForUser_whenMultiUserSettingDisabled_isIgnored() {
|
||||
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
|
||||
|
||||
mTrampoline.startServiceForUser(10);
|
||||
|
||||
verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startServiceForUser_whenMultiUserSettingEnabled_callsBackupManagerService() {
|
||||
Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
|
||||
|
||||
mTrampoline.startServiceForUser(10);
|
||||
|
||||
verify(mBackupManagerServiceMock).startServiceForUser(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializeService_forUserSystem_successfullyInitialized() {
|
||||
mTrampoline.initializeService(UserHandle.USER_SYSTEM);
|
||||
|
||||
Reference in New Issue
Block a user