diff options
3 files changed, 272 insertions, 0 deletions
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 94ea01607714..7ec3d243529f 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -67,6 +67,8 @@ android_test { "mockito-target-minus-junit4", "platform-test-annotations", "truth", + "Nene", + "Harrier", ], libs: [ "android.test.base", diff --git a/packages/SettingsProvider/test/AndroidTest.xml b/packages/SettingsProvider/test/AndroidTest.xml index 0bf53ccf81a6..dccc2d398f12 100644 --- a/packages/SettingsProvider/test/AndroidTest.xml +++ b/packages/SettingsProvider/test/AndroidTest.xml @@ -14,6 +14,8 @@ limitations under the License. --> <configuration description="Run Settings Provider Tests."> + <option name="config-descriptor:metadata" key="parameter" value="multiuser" /> + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" /> <option name="restore-settings" value="true" /> @@ -30,5 +32,7 @@ <option name="package" value="com.android.providers.setting.test" /> <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> + <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" /> + <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" /> </test> </configuration> diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java new file mode 100644 index 000000000000..ca1e4c10d339 --- /dev/null +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2024 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.providers.settings; + +import static android.provider.Settings.Secure.ACCESSIBILITY_ENABLED; +import static android.provider.Settings.Secure.SYNC_PARENT_SOUNDS; +import static android.provider.Settings.System.RINGTONE; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.pm.PackageManager; +import android.support.test.uiautomator.UiDevice; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.bedstead.harrier.BedsteadJUnit4; +import com.android.bedstead.harrier.DeviceState; +import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile; +import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser; +import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile; +import com.android.bedstead.harrier.annotations.RequireFeature; +import com.android.bedstead.harrier.annotations.RequireRunOnInitialUser; +import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser; +import com.android.bedstead.nene.TestApis; +import com.android.bedstead.nene.users.UserReference; +import com.android.bedstead.nene.users.UserType; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; + +@RunWith(BedsteadJUnit4.class) +@RequireRunOnPrimaryUser +public class SettingsProviderMultiUsersTest { + + @ClassRule @Rule + public static final DeviceState sDeviceState = new DeviceState(); + + private static final String SETTINGS = "some_random_setting"; + + private static final String GET_SHELL_COMMAND = "settings get --user "; + private static final String SET_SHELL_COMMAND = "settings put --user "; + private static final String DELETE_SHELL_COMMAND = "settings delete --user "; + + private static final String SPACE_GLOBAL = "global"; + private static final String SPACE_SYSTEM = "system"; + private static final String SPACE_SECURE = "secure"; + + private static final String CLONE_TO_MANAGED_PROFILE_SETTING = ACCESSIBILITY_ENABLED; + private static final String CLONE_FROM_PARENT_SETTINGS = RINGTONE; + private static final String SYNC_FROM_PARENT_SETTINGS = SYNC_PARENT_SOUNDS; + + private UiDevice mUiDevice; + private UserReference mPrimaryUser; + + @Before + public void setUp() throws Exception { + mPrimaryUser = sDeviceState.initialUser(); + + mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + } + + @Test + @RequireFeature(PackageManager.FEATURE_MANAGED_USERS) + @EnsureHasWorkProfile + public void testSettings_workProfile() throws Exception { + UserReference profile = sDeviceState.workProfile(); + + // Settings.Global settings are shared between different users + assertSettingsShared(SPACE_GLOBAL, mPrimaryUser.id(), profile.id()); + // Settings.System and Settings.Secure settings can be different on different users + assertSettingsDifferent(SPACE_SYSTEM, mPrimaryUser.id(), profile.id()); + assertSettingsDifferent(SPACE_SECURE, mPrimaryUser.id(), profile.id()); + } + + @Test + @RequireFeature(PackageManager.FEATURE_MANAGED_USERS) + @RequireRunOnInitialUser + @EnsureHasSecondaryUser + public void testSettings_secondaryUser() throws Exception { + UserReference secondaryUser = sDeviceState.secondaryUser(); + + // Settings.Global settings are shared between different users + assertSettingsShared(SPACE_GLOBAL, mPrimaryUser.id(), secondaryUser.id()); + // Settings.System and Settings.Secure settings can be different on different users + assertSettingsDifferent(SPACE_SYSTEM, mPrimaryUser.id(), secondaryUser.id()); + assertSettingsDifferent(SPACE_SECURE, mPrimaryUser.id(), secondaryUser.id()); + } + + private void assertSettingsDifferent(String type, int userId1, int userId2) throws Exception { + // reset settings + setSetting(type, SETTINGS, "noValue", userId2); + waitForIdle(); + + // set the value with user 1 + setSetting(type, SETTINGS, "value1", userId1); + waitForIdle(); + + // check no value with user 2 + String value = getSetting(type, SETTINGS, userId2); + assertThat(value).startsWith("noValue"); + + // set the value with user 2 + setSetting(type, SETTINGS, "value2", userId2); + waitForIdle(); + + // check the value with user 1 is not changed + value = getSetting(type, SETTINGS, userId1); + assertThat(value).startsWith("value1"); + } + + private void assertSettingsShared(String type, int userId1, int userId2) throws Exception { + // reset settings + setSetting(type, SETTINGS, "noValue", userId2); + waitForIdle(); + + // set the value with user 1 + setSetting(type, SETTINGS, "value1", userId1); + waitForIdle(); + + // check no value with user 2 + String value = getSetting(type, SETTINGS, userId2); + assertThat(value).startsWith("value1"); + + // set the value with user 2 + setSetting(type, SETTINGS, "value2", userId2); + waitForIdle(); + + // check the value with user 1 is not changed + value = getSetting(type, SETTINGS, userId1); + assertThat(value).startsWith("value2"); + } + + @Test + @RequireFeature(PackageManager.FEATURE_MANAGED_USERS) + @EnsureHasNoWorkProfile + public void testSettings_profile_cloneToManagedProfile() throws Exception { + assertSettingsCloned(SPACE_SECURE, CLONE_TO_MANAGED_PROFILE_SETTING, false); + } + + @Test + @RequireFeature(PackageManager.FEATURE_MANAGED_USERS) + @EnsureHasNoWorkProfile + public void testSettings_profile_cloneFromParent() throws Exception { + assertSettingsCloned(SPACE_SYSTEM, CLONE_FROM_PARENT_SETTINGS, true); + } + + private void assertSettingsCloned(String type, String name, boolean isSyncSettings) + throws Exception { + boolean resetSyncValue = false; + if (isSyncSettings) { + // set to sync settings from parent + final String oldSyncValue = + getSetting(SPACE_SECURE, SYNC_FROM_PARENT_SETTINGS, mPrimaryUser.id()); + resetSyncValue = oldSyncValue.startsWith("0"); + if (resetSyncValue) { + setSetting(SPACE_SECURE, SYNC_FROM_PARENT_SETTINGS, "1", mPrimaryUser.id()); + waitForIdle(); + } + } + + final String oldValue = getSetting(type, name, mPrimaryUser.id()); + + try (UserReference myProfile = TestApis.users().createUser() + .parent(mPrimaryUser) + .type(TestApis.users().supportedType(UserType.MANAGED_PROFILE_TYPE_NAME)) + .createAndStart()) { + String value = getSetting(type, name, myProfile.id()); + assertThat(value).isEqualTo(oldValue); + + String newValue; + if (isSyncSettings) { + newValue = generateNewValue(oldValue); + } else { + newValue = oldValue.startsWith("0") ? "1" : "0"; + } + + setSetting(type, name, newValue, mPrimaryUser.id()); + waitForIdle(); + + value = getSetting(type, name, myProfile.id()); + assertThat(value).startsWith(newValue); + } finally { + // reset settings + setSetting(type, name, oldValue, mPrimaryUser.id()); + if (resetSyncValue) { + setSetting(SPACE_SECURE, SYNC_FROM_PARENT_SETTINGS, "0", mPrimaryUser.id()); + } + } + } + + private String generateNewValue(String oldValue) { + String newValue = oldValue.replace("\n", ""); + if (newValue.endsWith("0")) { + final int size = newValue.length(); + newValue = newValue.substring(0, size - 1) + "1"; + } else { + final int size = newValue.length(); + newValue = newValue.substring(0, size - 1) + "0"; + } + return newValue; + } + + @Test + @RequireRunOnInitialUser + @EnsureHasSecondaryUser + public void testSettings_stopAndRestartSecondaryUser() throws Exception { + UserReference secondaryUser = sDeviceState.secondaryUser(); + + assertSettingsDifferent(SPACE_SECURE, mPrimaryUser.id(), secondaryUser.id()); + + secondaryUser.stop(); + + assertSettingsDifferent(SPACE_SECURE, mPrimaryUser.id(), secondaryUser.id()); + + secondaryUser.start(); + + assertSettingsDifferent(SPACE_SECURE, mPrimaryUser.id(), secondaryUser.id()); + } + + private void waitForIdle() { + final UiDevice uiDevice = UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()); + uiDevice.waitForIdle(); + } + + private String getSetting(String type, String name, int userId) throws Exception { + return executeShellCmd(GET_SHELL_COMMAND + userId + " " + type + " " + name); + } + + private void setSetting(String type, String name, String setting, int userId) + throws Exception { + setSetting(name, setting, userId + " " + type); + } + + private void setSetting(String name, String setting, String type) throws Exception { + if (setting == null || setting.equals("null")) { + executeShellCmd(DELETE_SHELL_COMMAND + type + " " + name); + } else { + setting = setting.replace("\n", ""); + executeShellCmd(SET_SHELL_COMMAND + type + " " + name + " " + setting); + } + } + + private String executeShellCmd(String command) throws IOException { + return mUiDevice.executeShellCommand(command); + } +} |