[Multi-user] Change device provisioned to per-user setup complete

Migrate from the global setting DEVICE_PROVISIONED to the per-user
setting USER_SETUP_COMPLETE. The logic for how the service handles
provisioned state is the same, only the setting we use to determine the
state is changed, as well as a rename to reflect this ("provisioned" ->
"setup complete").

ProvisionedObserver is now SetupObserver (move didn't persist b/c of
amount of changes to the file)

The BackupManagerMonitor log event id referencing provisioned will be
changed in a future CL as it requires an API change.

Bug: 121305979
Test: 1) atest RunBackupFrameworksServicesRoboTests
2) Reset device, go through SUW -> not setupComplete;
Finish SUW -> setupComplete (verify logging + dumpsys) and jobs
scheduled;
Perform backup (not blocked by setupComplete);
Add new user -> setupComplete only after finishing SUW

Change-Id: Iddf9ce7853a13b2d51135047de1b270af7034c06
This commit is contained in:
Annie Meng
2019-01-04 20:31:11 +00:00
parent 180e51535c
commit 443143af4b
7 changed files with 277 additions and 106 deletions

View File

@@ -110,9 +110,9 @@ import com.android.server.backup.internal.ClearDataObserver;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
import com.android.server.backup.internal.PerformInitializeTask;
import com.android.server.backup.internal.ProvisionedObserver;
import com.android.server.backup.internal.RunBackupReceiver;
import com.android.server.backup.internal.RunInitializeReceiver;
import com.android.server.backup.internal.SetupObserver;
import com.android.server.backup.keyvalue.BackupRequest;
import com.android.server.backup.params.AdbBackupParams;
import com.android.server.backup.params.AdbParams;
@@ -261,7 +261,7 @@ public class UserBackupManagerService {
private IBackupManager mBackupManagerBinder;
private boolean mEnabled; // access to this is synchronized on 'this'
private boolean mProvisioned;
private boolean mSetupComplete;
private boolean mAutoRestore;
private PendingIntent mRunBackupIntent;
@@ -315,9 +315,6 @@ public class UserBackupManagerService {
private ActiveRestoreSession mActiveRestoreSession;
// Watch the device provisioning operation during setup
private ContentObserver mProvisionedObserver;
/**
* mCurrentOperations contains the list of currently active operations.
*
@@ -468,15 +465,19 @@ public class UserBackupManagerService {
// Set up our bookkeeping
final ContentResolver resolver = context.getContentResolver();
mProvisioned = Settings.Global.getInt(resolver,
Settings.Global.DEVICE_PROVISIONED, 0) != 0;
mSetupComplete =
Settings.Secure.getIntForUser(
resolver, Settings.Secure.USER_SETUP_COMPLETE, 0, mUserId)
!= 0;
mAutoRestore = Settings.Secure.getInt(resolver,
Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
ContentObserver setupObserver = new SetupObserver(this, mBackupHandler);
resolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
false, mProvisionedObserver);
Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
/* notifyForDescendents */ false,
setupObserver,
mUserId);
mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
mBaseStateDir.mkdirs();
@@ -550,6 +551,10 @@ public class UserBackupManagerService {
mUserBackupThread.quit();
}
public int getUserId() {
return mUserId;
}
public BackupManagerConstants getConstants() {
return mConstants;
}
@@ -619,12 +624,12 @@ public class UserBackupManagerService {
mEnabled = enabled;
}
public boolean isProvisioned() {
return mProvisioned;
public boolean isSetupComplete() {
return mSetupComplete;
}
public void setProvisioned(boolean provisioned) {
mProvisioned = provisioned;
public void setSetupComplete(boolean setupComplete) {
mSetupComplete = setupComplete;
}
public PowerManager.WakeLock getWakelock() {
@@ -1577,11 +1582,16 @@ public class UserBackupManagerService {
throw new IllegalArgumentException("No packages are provided for backup");
}
if (!mEnabled || !mProvisioned) {
Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
if (!mEnabled || !mSetupComplete) {
Slog.i(
TAG,
"Backup requested but enabled="
+ mEnabled
+ " setupComplete="
+ mSetupComplete);
BackupObserverUtils.sendBackupFinished(observer,
BackupManager.ERROR_BACKUP_NOT_ALLOWED);
final int logTag = mProvisioned
final int logTag = mSetupComplete
? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
: BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
@@ -2016,13 +2026,13 @@ public class UserBackupManagerService {
FullBackupEntry entry = null;
long latency = fullBackupInterval;
if (!mEnabled || !mProvisioned) {
if (!mEnabled || !mSetupComplete) {
// Backups are globally disabled, so don't proceed. We also don't reschedule
// the job driving automatic backups; that job will be scheduled again when
// the user enables backup.
if (MORE_DEBUG) {
Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+ " p=" + mProvisioned + "; ignoring");
Slog.i(TAG, "beginFullBackup but enabled=" + mEnabled
+ " setupComplete=" + mSetupComplete + "; ignoring");
}
return false;
}
@@ -2423,12 +2433,6 @@ public class UserBackupManagerService {
}
}
/** Returns {@code true} if the system user has gone through SUW. */
public boolean deviceIsProvisioned() {
final ContentResolver resolver = mContext.getContentResolver();
return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
}
/**
* Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
* the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
@@ -2461,8 +2465,7 @@ public class UserBackupManagerService {
long oldId = Binder.clearCallingIdentity();
try {
// Doesn't make sense to do a full backup prior to setup
if (!deviceIsProvisioned()) {
if (!mSetupComplete) {
Slog.i(TAG, "Backup not supported before setup");
return;
}
@@ -2588,9 +2591,7 @@ public class UserBackupManagerService {
long oldId = Binder.clearCallingIdentity();
try {
// Check whether the device has been provisioned -- we don't handle
// full restores prior to completing the setup process.
if (!deviceIsProvisioned()) {
if (!mSetupComplete) {
Slog.i(TAG, "Full restore not permitted before setup");
return;
}
@@ -2745,7 +2746,7 @@ public class UserBackupManagerService {
}
synchronized (mQueueLock) {
if (enable && !wasEnabled && mProvisioned) {
if (enable && !wasEnabled && mSetupComplete) {
// if we've just been enabled, start scheduling backup passes
KeyValueBackupJob.schedule(mContext, mConstants);
scheduleNextFullBackupJob(0);
@@ -2758,7 +2759,7 @@ public class UserBackupManagerService {
// This also constitutes an opt-out, so we wipe any data for
// this device from the backend. We start that process with
// an alarm in order to guarantee wakelock states.
if (wasEnabled && mProvisioned) {
if (wasEnabled && mSetupComplete) {
// NOTE: we currently flush every registered transport, not just
// the currently-active one.
List<String> transportNames = new ArrayList<>();
@@ -3455,7 +3456,7 @@ public class UserBackupManagerService {
private void dumpInternal(PrintWriter pw) {
synchronized (mQueueLock) {
pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+ " / " + (!mSetupComplete ? "not " : "") + "setup complete / "
+ (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
if (mBackupRunning) pw.println("Backup currently running");

View File

@@ -317,16 +317,16 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
int backupRunStatus = BackupManager.SUCCESS;
try {
if (!backupManagerService.isEnabled() || !backupManagerService.isProvisioned()) {
if (!backupManagerService.isEnabled() || !backupManagerService.isSetupComplete()) {
// Backups are globally disabled, so don't proceed.
if (DEBUG) {
Slog.i(TAG, "full backup requested but enabled=" + backupManagerService
.isEnabled()
+ " provisioned=" + backupManagerService.isProvisioned()
+ " setupComplete=" + backupManagerService.isSetupComplete()
+ "; ignoring");
}
int monitoringEvent;
if (backupManagerService.isProvisioned()) {
if (backupManagerService.isSetupComplete()) {
monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED;
} else {
monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2017 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.internal;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.database.ContentObserver;
import android.os.Handler;
import android.util.Slog;
import com.android.server.backup.KeyValueBackupJob;
import com.android.server.backup.UserBackupManagerService;
public class ProvisionedObserver extends ContentObserver {
private UserBackupManagerService backupManagerService;
public ProvisionedObserver(
UserBackupManagerService backupManagerService, Handler handler) {
super(handler);
this.backupManagerService = backupManagerService;
}
public void onChange(boolean selfChange) {
final boolean wasProvisioned = backupManagerService.isProvisioned();
final boolean isProvisioned = backupManagerService.deviceIsProvisioned();
// latch: never unprovision
backupManagerService.setProvisioned(wasProvisioned || isProvisioned);
if (MORE_DEBUG) {
Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
+ " is=" + isProvisioned + " now=" + backupManagerService.isProvisioned());
}
synchronized (backupManagerService.getQueueLock()) {
if (backupManagerService.isProvisioned() && !wasProvisioned
&& backupManagerService.isEnabled()) {
// we're now good to go, so start the backup alarms
if (MORE_DEBUG) {
Slog.d(TAG, "Now provisioned, so starting backups");
}
KeyValueBackupJob.schedule(backupManagerService.getContext(),
backupManagerService.getConstants());
backupManagerService.scheduleNextFullBackupJob(0);
}
}
}
}

View File

@@ -59,7 +59,8 @@ public class RunBackupReceiver extends BroadcastReceiver {
} else {
// Don't run backups now if we're disabled or not yet
// fully set up.
if (backupManagerService.isEnabled() && backupManagerService.isProvisioned()) {
if (backupManagerService.isEnabled()
&& backupManagerService.isSetupComplete()) {
if (!backupManagerService.isBackupRunning()) {
if (DEBUG) {
Slog.v(TAG, "Running a backup pass");
@@ -77,8 +78,8 @@ public class RunBackupReceiver extends BroadcastReceiver {
Slog.i(TAG, "Backup time but one already running");
}
} else {
Slog.w(TAG, "Backup pass but e=" + backupManagerService.isEnabled() + " p="
+ backupManagerService.isProvisioned());
Slog.w(TAG, "Backup pass but enabled=" + backupManagerService.isEnabled()
+ " setupComplete=" + backupManagerService.isSetupComplete());
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2018 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.internal;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.provider.Settings;
import android.util.Slog;
import com.android.server.backup.KeyValueBackupJob;
import com.android.server.backup.UserBackupManagerService;
/**
* A {@link ContentObserver} for changes to the setting {@link Settings.Secure#USER_SETUP_COMPLETE}
* for a particular user.
*/
public class SetupObserver extends ContentObserver {
private final UserBackupManagerService mUserBackupManagerService;
private final Context mContext;
private final int mUserId;
public SetupObserver(UserBackupManagerService userBackupManagerService, Handler handler) {
super(handler);
mUserBackupManagerService = userBackupManagerService;
mContext = userBackupManagerService.getContext();
mUserId = userBackupManagerService.getUserId();
}
/**
* Callback that executes when the setting {@link Settings.Secure#USER_SETUP_COMPLETE} changes
* for the user {@link #mUserId}. If the user is newly setup and backup is enabled, then we
* schedule a key value and full backup job for the user. If the user was previously setup and
* now the setting has changed to {@code false}, we don't reset the state as having gone through
* setup is a non-reversible action.
*/
public void onChange(boolean selfChange) {
boolean previousSetupComplete = mUserBackupManagerService.isSetupComplete();
boolean newSetupComplete =
Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE,
0,
mUserId)
!= 0;
boolean resolvedSetupComplete = previousSetupComplete || newSetupComplete;
mUserBackupManagerService.setSetupComplete(resolvedSetupComplete);
if (MORE_DEBUG) {
Slog.d(
TAG,
"Setup complete change: was="
+ previousSetupComplete
+ " new="
+ newSetupComplete
+ " resolved="
+ resolvedSetupComplete);
}
synchronized (mUserBackupManagerService.getQueueLock()) {
// Start backup if the user is newly setup and backup is enabled.
if (resolvedSetupComplete
&& !previousSetupComplete
&& mUserBackupManagerService.isEnabled()) {
if (MORE_DEBUG) {
Slog.d(TAG, "Setup complete so starting backups");
}
KeyValueBackupJob.schedule(mContext, mUserBackupManagerService.getConstants());
mUserBackupManagerService.scheduleNextFullBackupJob(0);
}
}
}
}

View File

@@ -829,7 +829,7 @@ public class UserBackupManagerServiceTest {
public void testRequestBackup_whenNotProvisioned() throws Exception {
mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setProvisioned(false);
backupManagerService.setSetupComplete(false);
int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -848,7 +848,7 @@ public class UserBackupManagerServiceTest {
setUpCurrentTransport(mTransportManager, mTransport.unregistered());
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setEnabled(true);
backupManagerService.setProvisioned(true);
backupManagerService.setSetupComplete(true);
int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -868,7 +868,7 @@ public class UserBackupManagerServiceTest {
setUpCurrentTransport(mTransportManager, mTransport);
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setEnabled(true);
backupManagerService.setProvisioned(true);
backupManagerService.setSetupComplete(true);
// Haven't set PACKAGE_1 as eligible
int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
@@ -965,7 +965,7 @@ public class UserBackupManagerServiceTest {
private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
UserBackupManagerService backupManagerService = createUserBackupManagerServiceAndRunTasks();
backupManagerService.setEnabled(true);
backupManagerService.setProvisioned(true);
backupManagerService.setSetupComplete(true);
return backupManagerService;
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2018 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.internal;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import com.android.server.backup.KeyValueBackupJob;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.testing.BackupManagerServiceTestUtils;
import com.android.server.backup.testing.TestUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.io.File;
/**
* Tests verifying the interaction between {@link SetupObserver} and {@link
* UserBackupManagerService}.
*/
@RunWith(RobolectricTestRunner.class)
@Presubmit
public class SetupObserverTest {
private static final String TAG = "SetupObserverTest";
private static final int USER_ID = 10;
@Mock private TransportManager mTransportManager;
private Context mContext;
private UserBackupManagerService mUserBackupManagerService;
private HandlerThread mHandlerThread;
/** Setup state. */
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mHandlerThread = BackupManagerServiceTestUtils.startSilentBackupThread(TAG);
mUserBackupManagerService =
BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
USER_ID,
mContext,
mHandlerThread,
new File(mContext.getDataDir(), "test1"),
new File(mContext.getDataDir(), "test2"),
mTransportManager);
}
/** Test observer handles changes from not setup -> setup correctly. */
@Test
public void testOnChange_whenNewlySetup_updatesState() throws Exception {
SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
mUserBackupManagerService.setSetupComplete(false);
changeSetupCompleteSettingForUser(true, USER_ID);
setupObserver.onChange(true);
assertThat(mUserBackupManagerService.isSetupComplete()).isTrue();
}
/** Test observer handles changes from setup -> not setup correctly. */
@Test
public void testOnChange_whenPreviouslySetup_doesNotUpdateState() throws Exception {
SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
mUserBackupManagerService.setSetupComplete(true);
changeSetupCompleteSettingForUser(false, USER_ID);
setupObserver.onChange(true);
assertThat(mUserBackupManagerService.isSetupComplete()).isTrue();
}
/** Test observer handles changes from not setup -> not setup correctly. */
@Test
public void testOnChange_whenNotPreviouslySetup_doesNotUpdateStateIfNoChange()
throws Exception {
SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
mUserBackupManagerService.setSetupComplete(false);
changeSetupCompleteSettingForUser(false, USER_ID);
setupObserver.onChange(true);
assertThat(mUserBackupManagerService.isSetupComplete()).isFalse();
}
/** Test observer handles changes from not setup -> setup correctly. */
@Test
public void testOnChange_whenNewlySetup_schedulesBackup() throws Exception {
SetupObserver setupObserver = new SetupObserver(mUserBackupManagerService, new Handler());
mUserBackupManagerService.setSetupComplete(false);
changeSetupCompleteSettingForUser(true, USER_ID);
// Setup conditions for a full backup job to be scheduled.
mUserBackupManagerService.setEnabled(true);
mUserBackupManagerService.enqueueFullBackup("testPackage", /* lastBackedUp */ 0);
// Clear the handler of all pending tasks. This is to prevent the below assertion on the
// handler from encountering false positives due to other tasks being scheduled as part of
// setup work.
TestUtils.runToEndOfTasks(mHandlerThread.getLooper());
setupObserver.onChange(true);
assertThat(KeyValueBackupJob.isScheduled()).isTrue();
// Verifies that the full backup job is scheduled. The job is scheduled via a posted message
// on the backup handler so we verify that a message exists.
assertThat(mUserBackupManagerService.getBackupHandler().hasMessagesOrCallbacks()).isTrue();
}
private void changeSetupCompleteSettingForUser(boolean value, int userId) {
Settings.Secure.putIntForUser(
mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE,
value ? 1 : 0,
userId);
}
}