From a4fc23140a9b9c7b6f95c57a83a8da72d0d1400d Mon Sep 17 00:00:00 2001 From: Stanley Tng Date: Wed, 20 Jun 2018 15:32:02 -0700 Subject: [PATCH 1/5] Add Feature Flag for Hearing Aid Profile Using the Settings App-Developer Options-Feature Flag, allow the user to enable or disable the Hearing Aid Profile. Bug: 116725094 Bug: 116044083 Test: Manual testing using Settings App Change-Id: I16b51d7feabc914219c24731eb39a23bd1782571 Merged-In: I16b51d7feabc914219c24731eb39a23bd1782571 (cherry picked from commit 068c2547f6f877273b09bf3ace5b4a4b61c7647a) --- .../android/settings/core/FeatureFlags.java | 1 + .../featureflags/FeatureFlagPersistent.java | 67 ++++++++ .../featureflags/FeatureFlagPreference.java | 17 +- .../FeatureFlagPersistentTest.java | 162 ++++++++++++++++++ 4 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 src/com/android/settings/development/featureflags/FeatureFlagPersistent.java create mode 100644 tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPersistentTest.java diff --git a/src/com/android/settings/core/FeatureFlags.java b/src/com/android/settings/core/FeatureFlags.java index e77c27b6a71..db941a6dd7c 100644 --- a/src/com/android/settings/core/FeatureFlags.java +++ b/src/com/android/settings/core/FeatureFlags.java @@ -26,4 +26,5 @@ public class FeatureFlags { public static final String BLUETOOTH_WHILE_DRIVING = "settings_bluetooth_while_driving"; public static final String DATA_USAGE_SETTINGS_V2 = "settings_data_usage_v2"; public static final String AUDIO_SWITCHER_SETTINGS = "settings_audio_switcher"; + public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; } diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPersistent.java b/src/com/android/settings/development/featureflags/FeatureFlagPersistent.java new file mode 100644 index 00000000000..731dc6f2814 --- /dev/null +++ b/src/com/android/settings/development/featureflags/FeatureFlagPersistent.java @@ -0,0 +1,67 @@ +/* + * 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.settings.development.featureflags; + +import android.content.Context; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; +import android.util.Log; + +import com.android.settings.core.FeatureFlags; + +import java.util.HashSet; + +import androidx.annotation.VisibleForTesting; + +/** + * Helper class to get feature persistent flag information. + */ +public class FeatureFlagPersistent { + private static final HashSet PERSISTENT_FLAGS; + static { + PERSISTENT_FLAGS = new HashSet<>(); + PERSISTENT_FLAGS.add(FeatureFlags.HEARING_AID_SETTINGS); + } + + public static boolean isEnabled(Context context, String feature) { + String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + feature); + if (!TextUtils.isEmpty(value)) { + return Boolean.parseBoolean(value); + } else { + return FeatureFlagUtils.isEnabled(context, feature); + } + } + + public static void setEnabled(Context context, String feature, boolean enabled) { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + feature, enabled ? "true" : "false"); + FeatureFlagUtils.setEnabled(context, feature, enabled); + } + + public static boolean isPersistent(String feature) { + return PERSISTENT_FLAGS.contains(feature); + } + + /** + * Returns all persistent flags in their raw form. + */ + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + static HashSet getAllPersistentFlags() { + return PERSISTENT_FLAGS; + } +} + diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java index a8377090e8c..ebf35de1f31 100644 --- a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java +++ b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java @@ -18,25 +18,38 @@ package com.android.settings.development.featureflags; import android.content.Context; import android.util.FeatureFlagUtils; +import android.util.Log; import androidx.preference.SwitchPreference; public class FeatureFlagPreference extends SwitchPreference { private final String mKey; + private final boolean mIsPersistent; public FeatureFlagPreference(Context context, String key) { super(context); mKey = key; setKey(key); setTitle(key); - setCheckedInternal(FeatureFlagUtils.isEnabled(context, mKey)); + mIsPersistent = FeatureFlagPersistent.isPersistent(key); + boolean isFeatureEnabled; + if (mIsPersistent) { + isFeatureEnabled = FeatureFlagPersistent.isEnabled(context, key); + } else { + isFeatureEnabled = FeatureFlagUtils.isEnabled(context, key); + } + setCheckedInternal(isFeatureEnabled); } @Override public void setChecked(boolean isChecked) { setCheckedInternal(isChecked); - FeatureFlagUtils.setEnabled(getContext(), mKey, isChecked); + if (mIsPersistent) { + FeatureFlagPersistent.setEnabled(getContext(), mKey, isChecked); + } else { + FeatureFlagUtils.setEnabled(getContext(), mKey, isChecked); + } } private void setCheckedInternal(boolean isChecked) { diff --git a/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPersistentTest.java b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPersistentTest.java new file mode 100644 index 00000000000..c9f452de7d6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPersistentTest.java @@ -0,0 +1,162 @@ +/* + * 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.settings.development.featureflags; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.settings.development.featureflags.FeatureFlagPersistent; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import android.content.Context; +import android.os.SystemProperties; +import android.provider.Settings; +import android.util.FeatureFlagUtils; +import android.util.Log; + +@RunWith(SettingsRobolectricTestRunner.class) +public class FeatureFlagPersistentTest { + + private static final String TEST_FEATURE_NAME = "test_feature"; + + private static final String PERSISTENT_FALSE_NAME = "false_persistent"; + private static final String PERSISTENT_TRUE_NAME = "true_persistent"; + private static final String VOLATILE_FALSE_NAME = "volatile_false_volatile"; + private static final String VOLATILE_TRUE_NAME = "volatile_true_volatile"; + + private Context mContext; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + + FeatureFlagPersistent.getAllPersistentFlags().add(TEST_FEATURE_NAME); + FeatureFlagUtils.getAllFeatureFlags().put(TEST_FEATURE_NAME, "false"); + + FeatureFlagUtils.getAllFeatureFlags().put(VOLATILE_FALSE_NAME, "false"); + FeatureFlagUtils.getAllFeatureFlags().put(VOLATILE_TRUE_NAME, "true"); + + FeatureFlagPersistent.getAllPersistentFlags().add(PERSISTENT_FALSE_NAME); + FeatureFlagUtils.getAllFeatureFlags().put(PERSISTENT_FALSE_NAME, "false"); + + FeatureFlagPersistent.getAllPersistentFlags().add(PERSISTENT_TRUE_NAME); + FeatureFlagUtils.getAllFeatureFlags().put(PERSISTENT_TRUE_NAME, "true"); + } + + @After + public void tearDown() { + cleanup(PERSISTENT_FALSE_NAME); + cleanup(PERSISTENT_TRUE_NAME); + cleanup(VOLATILE_FALSE_NAME); + cleanup(VOLATILE_TRUE_NAME); + cleanup(TEST_FEATURE_NAME); + } + + private void cleanup(String flagName) { + Settings.Global.putString(mContext.getContentResolver(), flagName, ""); + SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + flagName, ""); + SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + flagName, ""); + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + flagName, ""); + } + + /** + * Test to verify a non-persistent flag is indeed not persistent. + */ + @Test + public void isPersistent_notPersistent_shouldReturnFalse() { + assertThat(FeatureFlagPersistent.isPersistent(VOLATILE_FALSE_NAME)).isFalse(); + } + + /** + * Test to verify a persistent flag is indeed persistent. + */ + @Test + public void isPersistent_persistent_shouldReturnTrue() { + assertThat(FeatureFlagPersistent.isPersistent(PERSISTENT_TRUE_NAME)).isTrue(); + } + + /** + * Test to verify a persistent flag that is enabled should return true. + */ + @Test + public void isEnabled_enabled_shouldReturnTrue() { + assertThat(FeatureFlagPersistent.isEnabled(mContext, PERSISTENT_TRUE_NAME)).isTrue(); + } + + /** + * Test to verify a persistent flag that is disabled should return false. + */ + @Test + public void isEnabled_disabled_shouldReturnFalse() { + assertThat(FeatureFlagPersistent.isEnabled(mContext, PERSISTENT_FALSE_NAME)).isFalse(); + } + + /** + * Test to verify a persistent flag that has an enabled in system property should return true. + */ + @Test + public void isEnabled_sysPropEnabled_shouldReturnTrue() { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME, "true"); + FeatureFlagUtils.setEnabled(mContext, TEST_FEATURE_NAME, false); + + assertThat(FeatureFlagPersistent.isEnabled(mContext, TEST_FEATURE_NAME)).isTrue(); + } + + /** + * Test to verify a persistent flag that is disabled in system property should return false. + */ + @Test + public void isEnabled_sysPropDisabled_shouldReturnFalse() { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME, "false"); + FeatureFlagUtils.setEnabled(mContext, TEST_FEATURE_NAME, true); + + assertThat(FeatureFlagPersistent.isEnabled(mContext, TEST_FEATURE_NAME)).isFalse(); + } + + /** + * Test to verify setting persistent flag to enable works. + */ + @Test + public void setEnabled_sysPropTrue_shouldSetValues() { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME, ""); + + FeatureFlagPersistent.setEnabled(mContext, TEST_FEATURE_NAME, true); + + assertThat(SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME)).isEqualTo("true"); + assertThat(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME)).isTrue(); + } + + /** + * Test to verify setting persistent flag to disable works. + */ + @Test + public void setEnabled_sysPropFalse_shouldSetValues() { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME, ""); + + FeatureFlagPersistent.setEnabled(mContext, TEST_FEATURE_NAME, false); + + assertThat(SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME)).isEqualTo("false"); + assertThat(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME)).isFalse(); + } +} + From 098d3a79fd075fc51eec29a687389ff109898219 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Mon, 15 Oct 2018 14:30:34 -0700 Subject: [PATCH 2/5] Fix ActionDisabledByAdminDialogHelperTest - Revert user handling in setAdminSupportDetails to before my changes. - Implement getUserProfiles in Settings-shadows as the the Settings-shadows do not forwarded to profiles the robolectric-default shadows Test: make RunSettingsRoboTests Change-Id: Idfd4722fe55a17c57d336db943904eb1aeb6e7ce --- .../ActionDisabledByAdminDialogHelper.java | 24 +++++++++---------- .../testutils/shadow/ShadowUserManager.java | 10 ++++++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java index f5010f9d5d4..30a036c3301 100644 --- a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java +++ b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java @@ -44,6 +44,8 @@ import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; +import libcore.util.NonNull; + import java.util.Objects; /** @@ -61,14 +63,18 @@ public class ActionDisabledByAdminDialogHelper { mActivity = activity; } - private @UserIdInt int getEnforcementAdminUserId() { - if (mEnforcedAdmin.user == null) { + private @UserIdInt int getEnforcementAdminUserId(@NonNull EnforcedAdmin admin) { + if (admin.user == null) { return UserHandle.USER_NULL; } else { - return mEnforcedAdmin.user.getIdentifier(); + return admin.user.getIdentifier(); } } + private @UserIdInt int getEnforcementAdminUserId() { + return getEnforcementAdminUserId(mEnforcedAdmin); + } + public AlertDialog.Builder prepareDialogBuilder(String restriction, EnforcedAdmin enforcedAdmin) { mEnforcedAdmin = enforcedAdmin; @@ -170,18 +176,11 @@ public class ActionDisabledByAdminDialogHelper { return; } - final int userId; - if (enforcedAdmin.user == null) { - userId = UserHandle.USER_NULL; - } else { - userId = enforcedAdmin.user.getIdentifier(); - } - final DevicePolicyManager dpm = (DevicePolicyManager) activity.getSystemService( Context.DEVICE_POLICY_SERVICE); if (!RestrictedLockUtilsInternal.isAdminInCurrentUserOrProfile(activity, enforcedAdmin.component) || !RestrictedLockUtils.isCurrentUserOrProfile( - activity, userId)) { + activity, getEnforcementAdminUserId(enforcedAdmin))) { enforcedAdmin.component = null; } else { if (enforcedAdmin.user == null) { @@ -189,7 +188,8 @@ public class ActionDisabledByAdminDialogHelper { } CharSequence supportMessage = null; if (UserHandle.isSameApp(Process.myUid(), Process.SYSTEM_UID)) { - supportMessage = dpm.getShortSupportMessageForUser(enforcedAdmin.component, userId); + supportMessage = dpm.getShortSupportMessageForUser(enforcedAdmin.component, + getEnforcementAdminUserId(enforcedAdmin)); } if (supportMessage != null) { final TextView textView = root.findViewById(R.id.admin_support_msg); diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java index 3266a7fb1c6..bb691cb382d 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java @@ -88,6 +88,16 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager return ids; } + @Implementation + public List getUserProfiles(){ + int[] userIds = getProfileIds(UserHandle.myUserId(), true /* enabledOnly */); + List result = new ArrayList<>(userIds.length); + for (int userId : userIds) { + result.add(UserHandle.of(userId)); + } + return result; + } + @Implementation public int getCredentialOwnerProfile(@UserIdInt int userHandle) { return userHandle; From 2493587202d5bc0ea3de8916abaa3bca9bcb568d Mon Sep 17 00:00:00 2001 From: Raff Tsai Date: Tue, 16 Oct 2018 08:03:08 +0800 Subject: [PATCH 3/5] Use DayNight theme in factory reset page Change-Id: I3cc75a34aaec71c97d1dd83eaf40232de1fd4168 Fixes: 73738836 Test: Manual --- AndroidManifest.xml | 2 +- res/layout/master_clear.xml | 2 +- res/layout/master_clear_confirm.xml | 2 +- res/values/themes_suw.xml | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f60d99e219d..109f2b9e8f7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1560,7 +1560,7 @@ android:enabled="false" android:exported="true" android:taskAffinity="com.android.wizard" - android:theme="@style/SuwThemeGlif.Light" + android:theme="@style/SuwThemeGlif.DayNight" android:label="@string/lock_screen_notifications_interstitial_title" android:icon="@drawable/ic_suggested_notifications"> diff --git a/res/layout/master_clear.xml b/res/layout/master_clear.xml index 247eedc407f..8d4d6231a23 100644 --- a/res/layout/master_clear.xml +++ b/res/layout/master_clear.xml @@ -21,7 +21,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:theme="@style/SuwThemeGlifV3.Light" + android:theme="@style/SuwThemeGlifV3.DayNight" android:icon="@drawable/ic_delete_accent" app:suwHeaderText="@string/master_clear_title"> diff --git a/res/layout/master_clear_confirm.xml b/res/layout/master_clear_confirm.xml index 146fb770b8d..d513dbb3135 100644 --- a/res/layout/master_clear_confirm.xml +++ b/res/layout/master_clear_confirm.xml @@ -19,7 +19,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:theme="@style/SuwThemeGlifV3.Light" + android:theme="@style/SuwThemeGlifV3.DayNight" android:id="@+id/setup_wizard_layout" android:icon="@drawable/ic_delete_accent" app:suwHeaderText="@string/master_clear_confirm_title"> diff --git a/res/values/themes_suw.xml b/res/values/themes_suw.xml index f09b2b1427f..d66f917173b 100644 --- a/res/values/themes_suw.xml +++ b/res/values/themes_suw.xml @@ -17,7 +17,7 @@ - - -