From e4fe2f5b8134c007e29345ae11a64d37e8ef214a Mon Sep 17 00:00:00 2001 From: Guojing Yuan Date: Tue, 1 Oct 2024 21:59:31 +0000 Subject: [PATCH 01/12] [CDM][NLS] Check if the NLS service has an intent-filter Bug: 363248394 Test: CTS Flag: EXEMPT bugfix Change-Id: Ib79c219cde8d73a218ceb7911f4552d43e384d8e Merged-In: Ib79c219cde8d73a218ceb7911f4552d43e384d8e (cherry picked from commit 7ae59a42eb13f643d842525208619037c074371a) --- ...otificationAccessConfirmationActivity.java | 50 +++++++++++-------- ...icationAccessConfirmationActivityTest.java | 9 ++-- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java index 9ea8c58024f..74b8102ee2c 100644 --- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java +++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java @@ -31,13 +31,15 @@ import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; +import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; +import android.service.notification.NotificationListenerService; import android.text.TextUtils; import android.util.Slog; import android.view.WindowManager; @@ -48,6 +50,8 @@ import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import com.android.settings.R; +import java.util.List; + /** @hide */ public class NotificationAccessConfirmationActivity extends Activity implements DialogInterface { @@ -112,6 +116,31 @@ public class NotificationAccessConfirmationActivity extends Activity return; } + // Check NLS service info. + String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; + Intent NLSIntent = new Intent(NotificationListenerService.SERVICE_INTERFACE); + List matchedServiceList = getPackageManager().queryIntentServicesAsUser( + NLSIntent, /* flags */ 0, mUserId); + boolean hasNLSIntentFilter = false; + for (ResolveInfo service : matchedServiceList) { + if (service.serviceInfo.packageName.equals(mComponentName.getPackageName())) { + if (!requiredPermission.equals(service.serviceInfo.permission)) { + Slog.e(LOG_TAG, "Service " + mComponentName + " lacks permission " + + requiredPermission); + finish(); + return; + } + hasNLSIntentFilter = true; + break; + } + } + if (!hasNLSIntentFilter) { + Slog.e(LOG_TAG, "Service " + mComponentName + " lacks an intent-filter action " + + "for android.service.notification.NotificationListenerService."); + finish(); + return; + } + AlertController.AlertParams p = new AlertController.AlertParams(this); p.mTitle = getString( R.string.notification_listener_security_warning_title, @@ -146,19 +175,6 @@ public class NotificationAccessConfirmationActivity extends Activity } private void onAllow() { - String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; - try { - ServiceInfo serviceInfo = getPackageManager().getServiceInfo(mComponentName, 0); - if (!requiredPermission.equals(serviceInfo.permission)) { - Slog.e(LOG_TAG, - "Service " + mComponentName + " lacks permission " + requiredPermission); - return; - } - } catch (PackageManager.NameNotFoundException e) { - Slog.e(LOG_TAG, "Failed to get service info for " + mComponentName, e); - return; - } - mNm.setNotificationListenerAccessGranted(mComponentName, true); finish(); @@ -169,12 +185,6 @@ public class NotificationAccessConfirmationActivity extends Activity return AlertActivity.dispatchPopulateAccessibilityEvent(this, event); } - @Override - public void onBackPressed() { - // Suppress finishing the activity on back button press, - // consistently with the permission dialog behavior - } - @Override public void cancel() { finish(); diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java index 86631ffb2d4..788f853e19b 100644 --- a/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java +++ b/tests/robotests/src/com/android/settings/notification/NotificationAccessConfirmationActivityTest.java @@ -30,8 +30,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.widget.TextView; -import com.android.settings.R; - import com.google.common.base.Strings; import org.junit.Test; @@ -44,15 +42,14 @@ import org.robolectric.RuntimeEnvironment; public class NotificationAccessConfirmationActivityTest { @Test - public void start_showsDialog() { + public void start_withMissingIntentFilter_finishes() { ComponentName cn = new ComponentName("com.example", "com.example.SomeService"); installPackage(cn.getPackageName(), "X"); NotificationAccessConfirmationActivity activity = startActivityWithIntent(cn); - assertThat(activity.isFinishing()).isFalse(); - assertThat(getDialogText(activity)).isEqualTo( - activity.getString(R.string.notification_listener_security_warning_summary, "X")); + assertThat(getDialogText(activity)).isNull(); + assertThat(activity.isFinishing()).isTrue(); } @Test From d440f2ef05c20f81e1b1cad447cca427a25d77ec Mon Sep 17 00:00:00 2001 From: Guojing Yuan Date: Tue, 1 Oct 2024 21:59:31 +0000 Subject: [PATCH 02/12] [CDM][NLS] Check if the NLS service has an intent-filter Bug: 363248394 Test: CTS Flag: EXEMPT bugfix Change-Id: Ib79c219cde8d73a218ceb7911f4552d43e384d8e Merged-In: Ib79c219cde8d73a218ceb7911f4552d43e384d8e (cherry picked from commit 7ae59a42eb13f643d842525208619037c074371a) --- ...otificationAccessConfirmationActivity.java | 50 +++++++++++-------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java index 9ea8c58024f..74b8102ee2c 100644 --- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java +++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java @@ -31,13 +31,15 @@ import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; +import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; +import android.service.notification.NotificationListenerService; import android.text.TextUtils; import android.util.Slog; import android.view.WindowManager; @@ -48,6 +50,8 @@ import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import com.android.settings.R; +import java.util.List; + /** @hide */ public class NotificationAccessConfirmationActivity extends Activity implements DialogInterface { @@ -112,6 +116,31 @@ public class NotificationAccessConfirmationActivity extends Activity return; } + // Check NLS service info. + String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; + Intent NLSIntent = new Intent(NotificationListenerService.SERVICE_INTERFACE); + List matchedServiceList = getPackageManager().queryIntentServicesAsUser( + NLSIntent, /* flags */ 0, mUserId); + boolean hasNLSIntentFilter = false; + for (ResolveInfo service : matchedServiceList) { + if (service.serviceInfo.packageName.equals(mComponentName.getPackageName())) { + if (!requiredPermission.equals(service.serviceInfo.permission)) { + Slog.e(LOG_TAG, "Service " + mComponentName + " lacks permission " + + requiredPermission); + finish(); + return; + } + hasNLSIntentFilter = true; + break; + } + } + if (!hasNLSIntentFilter) { + Slog.e(LOG_TAG, "Service " + mComponentName + " lacks an intent-filter action " + + "for android.service.notification.NotificationListenerService."); + finish(); + return; + } + AlertController.AlertParams p = new AlertController.AlertParams(this); p.mTitle = getString( R.string.notification_listener_security_warning_title, @@ -146,19 +175,6 @@ public class NotificationAccessConfirmationActivity extends Activity } private void onAllow() { - String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; - try { - ServiceInfo serviceInfo = getPackageManager().getServiceInfo(mComponentName, 0); - if (!requiredPermission.equals(serviceInfo.permission)) { - Slog.e(LOG_TAG, - "Service " + mComponentName + " lacks permission " + requiredPermission); - return; - } - } catch (PackageManager.NameNotFoundException e) { - Slog.e(LOG_TAG, "Failed to get service info for " + mComponentName, e); - return; - } - mNm.setNotificationListenerAccessGranted(mComponentName, true); finish(); @@ -169,12 +185,6 @@ public class NotificationAccessConfirmationActivity extends Activity return AlertActivity.dispatchPopulateAccessibilityEvent(this, event); } - @Override - public void onBackPressed() { - // Suppress finishing the activity on back button press, - // consistently with the permission dialog behavior - } - @Override public void cancel() { finish(); From 9f9571a0786adc321d354a23bd7cf9750a536ae1 Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Fri, 2 Aug 2024 21:24:38 +0000 Subject: [PATCH 03/12] Move Contextual Search setting to AOSP. This only appears if the correpsonding system feature is enabled. Bug: 353715553 Test: Manual Flag: EXEMPT moving code from vendor to aosp Change-Id: Ib19db6345b907500dfe3d53c36700df428c0171e --- res/values/strings.xml | 8 +- res/xml/button_navigation_settings.xml | 3 +- .../system_navigation_gesture_settings.xml | 15 ++- ...tonNavigationSettingsAssistController.java | 6 + ...ionSettingsContextualSearchController.java | 70 ++++++++++++ ...avigationSettingsAssistControllerTest.java | 25 +++++ ...ettingsContextualSearchControllerTest.java | 104 ++++++++++++++++++ 7 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 src/com/android/settings/gestures/NavigationSettingsContextualSearchController.java create mode 100644 tests/robotests/src/com/android/settings/gestures/NavigationSettingsContextualSearchControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 5326fa5d7a9..d76ce702582 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -13690,7 +13690,6 @@ Need help? - @@ -13711,4 +13710,11 @@ No default set Add an account to get started + + + + Circle to Search + + Touch and hold the Home button or the navigation handle to search using the content on your screen. diff --git a/res/xml/button_navigation_settings.xml b/res/xml/button_navigation_settings.xml index 1de011c7089..1f895399b5b 100644 --- a/res/xml/button_navigation_settings.xml +++ b/res/xml/button_navigation_settings.xml @@ -28,7 +28,8 @@ settings:searchable="false" settings:dynamicColor="true" settings:lottie_imageAssetsFolder="button_nav_menu" - settings:lottie_rawRes="@raw/lottie_button_nav_menu"/> + settings:lottie_rawRes="@raw/lottie_button_nav_menu" + settings:controller="com.android.settings.gestures.ButtonNavigationSettingsAssistController"/> + + + + + + + diff --git a/aconfig/settings_flag_declarations.aconfig b/aconfig/settings_flag_declarations.aconfig index a9c7bd59f23..6eb1e0236de 100644 --- a/aconfig/settings_flag_declarations.aconfig +++ b/aconfig/settings_flag_declarations.aconfig @@ -63,3 +63,10 @@ flag { description: "Flag for all screens" bug: "323791114" } + +flag { + name: "catalyst_service" + namespace: "android_settings" + description: "Flag for catalyst service" + bug: "323791114" +} diff --git a/src/com/android/settings/SettingsService.kt b/src/com/android/settings/SettingsService.kt new file mode 100644 index 00000000000..2cd706b9b14 --- /dev/null +++ b/src/com/android/settings/SettingsService.kt @@ -0,0 +1,30 @@ +/* + * 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.settings + +import android.content.Intent +import android.os.IBinder +import com.android.settings.flags.Flags +import com.android.settingslib.service.PreferenceService + +/** Service to expose settings APIs. */ +class SettingsService : PreferenceService({ _, _, _ -> true }) { + + override fun onBind(intent: Intent): IBinder? { + return if (!Flags.catalystService()) null else super.onBind(intent) + } +} From a11a960167e985e43d215c47734d567b81face5e Mon Sep 17 00:00:00 2001 From: Riley Jones Date: Fri, 11 Oct 2024 23:09:22 +0000 Subject: [PATCH 08/12] Cleanup searchable items on Accessibility > System controls Flag: EXEMPT xml changes Bug: 354783604 Test: Manually verify conditions mentioned in bug Change-Id: I3fd6ddefc0bc666e12bff08046174be0c9e21904 --- res/xml/accessibility_system_controls.xml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/res/xml/accessibility_system_controls.xml b/res/xml/accessibility_system_controls.xml index 460214fa3b6..a8cb1bb28ab 100644 --- a/res/xml/accessibility_system_controls.xml +++ b/res/xml/accessibility_system_controls.xml @@ -20,6 +20,9 @@ android:persistent="false" android:title="@string/accessibility_system_controls_title"> + + + settings are NOT available. + The item is not specific to Accessibility. + The same entry is under Display & touch, which is unlikely to be removed, + so this is not searchable.--> + are available. + The item is not specific to Accessibility. + The same entry is under Display & touch, which is unlikely to be removed, + so this is not searchable. --> From 538a01023a4605b77dc4e8f49c6ad5b92e7ac80f Mon Sep 17 00:00:00 2001 From: Dongzhuo Zhang Date: Tue, 15 Oct 2024 02:59:21 +0000 Subject: [PATCH 09/12] Add ContactsStorageSettingsActivity to handle the android.provider.action.SET_DEFAULT_ACCOUNT intent Test:Manually tested to trigger the contacts storage settings page using intent to start activity. Bug: 368641291 Flag: com.android.settings.flags.enable_contacts_default_account_in_settings Change-Id: I83db4c5751c6fec523298a82b82468180906075c --- AndroidManifest.xml | 13 +++++++++++++ src/com/android/settings/Settings.java | 1 + .../settings/core/gateway/SettingsGateway.java | 4 +++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 6408ab11311..4b925bff4d7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5293,6 +5293,19 @@ android:value="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamConfirmDialog" /> + + + + + + + + Date: Tue, 15 Oct 2024 14:49:25 +0800 Subject: [PATCH 10/12] Fix multi-toggle flicker bug BUG: 343317785 Test: local tested Flag: com.android.settings.flags.enable_bluetooth_device_details_polish Change-Id: Ib5e700412c112ff6fa19b54f341fbd598c6b345c --- .../bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt index dd0012e90fb..3b7a5829bcc 100644 --- a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt +++ b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt @@ -58,6 +58,8 @@ class BluetoothDeviceDetailsViewModel( deviceSettingRepository.getDeviceSettingsConfig(cachedDevice) } + private val spatialAudioModel by lazy { spatialAudioInteractor.getDeviceSetting(cachedDevice) } + suspend fun getItems(fragment: FragmentTypeModel): List? = when (fragment) { is FragmentTypeModel.DeviceDetailsMainFragment -> items.await()?.mainItems @@ -81,7 +83,7 @@ class BluetoothDeviceDetailsViewModel( } return when (settingId) { DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE -> - spatialAudioInteractor.getDeviceSetting(cachedDevice) + spatialAudioModel else -> deviceSettingRepository.getDeviceSetting(cachedDevice, settingId) }.map { it?.toPreferenceModel() } } From 9c9c0a3d948f1fb77d1e19444d6a8ffabfc2d872 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Fri, 11 Oct 2024 15:24:58 +0800 Subject: [PATCH 11/12] [Catalyst] Network and Intenet screen migration - Add a flag for the migration - Add the skeleton of the screen Test: atest NetworkDashboardScreenTest Bug: 368355361 Flag: com.android.settings.flags.catalyst_network_provider_and_internet_screen Change-Id: I37622d318f2a52a7c60c9fa965b94fd135ce0b60 --- aconfig/catalyst/network_and_internet.aconfig | 9 ++++ .../network/NetworkDashboardFragment.java | 7 +++ .../network/NetworkDashboardScreen.kt | 47 +++++++++++++++++++ .../network/NetworkDashboardScreenTest.kt | 39 +++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 aconfig/catalyst/network_and_internet.aconfig create mode 100644 src/com/android/settings/network/NetworkDashboardScreen.kt create mode 100644 tests/robotests/src/com/android/settings/network/NetworkDashboardScreenTest.kt diff --git a/aconfig/catalyst/network_and_internet.aconfig b/aconfig/catalyst/network_and_internet.aconfig new file mode 100644 index 00000000000..a3e1784035d --- /dev/null +++ b/aconfig/catalyst/network_and_internet.aconfig @@ -0,0 +1,9 @@ +package: "com.android.settings.flags" +container: "system" + +flag { + name: "catalyst_network_provider_and_internet_screen" + namespace: "android_settings" + description: "Flag for Network & Internet" + bug: "323791114" +} diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java index ee7d440bcf3..3ba3dc6853c 100644 --- a/src/com/android/settings/network/NetworkDashboardFragment.java +++ b/src/com/android/settings/network/NetworkDashboardFragment.java @@ -19,6 +19,7 @@ import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.settings.R; @@ -115,4 +116,10 @@ public class NetworkDashboardFragment extends DashboardFragment implements return buildPreferenceControllers(context, null /* lifecycle */); } }; + + @Nullable + @Override + public String getPreferenceScreenBindingKey(@NonNull Context context) { + return NetworkDashboardScreen.KEY; + } } diff --git a/src/com/android/settings/network/NetworkDashboardScreen.kt b/src/com/android/settings/network/NetworkDashboardScreen.kt new file mode 100644 index 00000000000..dcdc37f386a --- /dev/null +++ b/src/com/android/settings/network/NetworkDashboardScreen.kt @@ -0,0 +1,47 @@ +/* + * 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.settings.network + +import android.content.Context +import com.android.settings.R +import com.android.settings.flags.Flags +import com.android.settingslib.metadata.ProvidePreferenceScreen +import com.android.settingslib.metadata.preferenceHierarchy +import com.android.settingslib.preference.PreferenceScreenCreator + +@ProvidePreferenceScreen +class NetworkDashboardScreen : PreferenceScreenCreator { + override val key: String + get() = KEY + + override val title: Int + get() = R.string.network_dashboard_title + + override val icon: Int + get() = R.drawable.ic_settings_wireless_filled + + override fun isFlagEnabled(context: Context) = Flags.catalystNetworkProviderAndInternetScreen() + + override fun hasCompleteHierarchy() = false + + override fun fragmentClass() = NetworkDashboardFragment::class.java + + override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) {} + + companion object { + const val KEY = "network_provider_and_internet_screen" + } +} diff --git a/tests/robotests/src/com/android/settings/network/NetworkDashboardScreenTest.kt b/tests/robotests/src/com/android/settings/network/NetworkDashboardScreenTest.kt new file mode 100644 index 00000000000..bb80f19d515 --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/NetworkDashboardScreenTest.kt @@ -0,0 +1,39 @@ +/* + * 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.settings.network + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.flags.Flags +import com.android.settingslib.preference.CatalystScreenTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class NetworkDashboardScreenTest : CatalystScreenTestCase() { + override val preferenceScreenCreator = NetworkDashboardScreen() + + override val flagName: String + get() = Flags.FLAG_CATALYST_NETWORK_PROVIDER_AND_INTERNET_SCREEN + + @Test + fun key() { + assertThat(preferenceScreenCreator.key).isEqualTo(NetworkDashboardScreen.KEY) + } + + override fun migration() { + } +} From 3ab6b197a9718ce381f673e9acdb9358c4e479fd Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Tue, 15 Oct 2024 16:43:05 +0800 Subject: [PATCH 12/12] Fix flaky tests due to shared system property The system property is shared within JVM, change system property in a test case might break test cases in another test class. To address the issue, introduce SystemProperty helper class to back up and restore the system properties in tests. Bug: 373177618 Flag: EXEMPT Test only Test: atest SettingsRoboTests Change-Id: I15539ce5ac425f35571d796baa25f259df1b601f --- tests/robotests/Android.bp | 5 +- .../FingerprintEnrollEnrollingTest.java | 11 +++- .../settings/display/DisplayScreenTest.kt | 16 ++--- .../settings/testutils/SystemProperty.kt | 64 +++++++++++++++++++ 4 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 tests/robotests/testutils/com/android/settings/testutils/SystemProperty.kt diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp index 84c84b0cf50..1cac3634cc0 100644 --- a/tests/robotests/Android.bp +++ b/tests/robotests/Android.bp @@ -109,7 +109,10 @@ android_robolectric_test { java_library { name: "Settings-robo-testutils", - srcs: ["testutils/**/*.java"], + srcs: [ + "testutils/**/*.java", + "testutils/**/*.kt", + ], libs: [ "Robolectric_all-target_upstream", "Settings-core", diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java index 8f983de8068..df2ab455c5b 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java @@ -63,12 +63,14 @@ import android.widget.TextView; import com.android.settings.R; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SystemProperty; import com.android.settings.widget.RingProgressBar; import com.airbnb.lottie.LottieAnimationView; import com.airbnb.lottie.LottieTask; import com.google.android.setupdesign.GlifLayout; +import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -111,15 +113,23 @@ public class FingerprintEnrollEnrollingTest { private final int[] mSfpsStageThresholds = new int[]{0, 9, 13, 19, 25}; private final int[] mUdfpsStageThresholds = new int[]{0, 13, 17, 22}; + private final SystemProperty mSystemProperty = new SystemProperty(); + private FingerprintEnrollEnrolling mActivity; private Context mContext; @Before public void setUp() { MockitoAnnotations.initMocks(this); + mSystemProperty.override("robolectric.createActivityContexts", "true"); FakeFeatureFactory.setupForTest(); } + @After + public void tearDown() { + mSystemProperty.close(); + } + @Test public void fingerprintUdfpsEnrollSuccessProgress_shouldNotVibrate() { initializeActivityFor(TYPE_UDFPS_OPTICAL); @@ -645,7 +655,6 @@ public class FingerprintEnrollEnrollingTest { } private void createActivity() { - System.setProperty("robolectric.createActivityContexts", "true"); final Bundle savedInstanceState = new Bundle(); savedInstanceState.putInt(KEY_STATE_PREVIOUS_ROTATION, Surface.ROTATION_90); diff --git a/tests/robotests/src/com/android/settings/display/DisplayScreenTest.kt b/tests/robotests/src/com/android/settings/display/DisplayScreenTest.kt index 6a7c2388622..61c3b192917 100644 --- a/tests/robotests/src/com/android/settings/display/DisplayScreenTest.kt +++ b/tests/robotests/src/com/android/settings/display/DisplayScreenTest.kt @@ -21,9 +21,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.internal.widget.LockPatternUtils import com.android.settings.flags.Flags import com.android.settings.testutils.FakeFeatureFactory +import com.android.settings.testutils.SystemProperty import com.android.settingslib.preference.CatalystScreenTestCase import com.google.common.truth.Truth.assertThat -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt @@ -66,16 +66,16 @@ class DisplayScreenTest : CatalystScreenTestCase() { assertThat(preferenceScreenCreator.isAvailable(contextWrapper)).isFalse() } - @Ignore("robolectric.createActivityContexts cause other test failure") override fun migration() { // avoid UnsupportedOperationException when getDisplay from context - System.setProperty("robolectric.createActivityContexts", "true") + SystemProperty("robolectric.createActivityContexts", "true").use { + val lockPatternUtils = + mock { on { isSecure(anyInt()) } doReturn true } + FakeFeatureFactory.setupForTest().securityFeatureProvider.stub { + on { getLockPatternUtils(any()) } doReturn lockPatternUtils + } - val lockPatternUtils = mock { on { isSecure(anyInt()) } doReturn true } - FakeFeatureFactory.setupForTest().securityFeatureProvider.stub { - on { getLockPatternUtils(any()) } doReturn lockPatternUtils + super.migration() } - - super.migration() } } diff --git a/tests/robotests/testutils/com/android/settings/testutils/SystemProperty.kt b/tests/robotests/testutils/com/android/settings/testutils/SystemProperty.kt new file mode 100644 index 00000000000..9c2574e52f9 --- /dev/null +++ b/tests/robotests/testutils/com/android/settings/testutils/SystemProperty.kt @@ -0,0 +1,64 @@ +/* + * 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.settings.testutils + +/** + * Helper class to override system properties. + * + * [System.setProperty] changes the static state in the JVM, which is shared by all tests. Hence, + * there is chance that test cases are dependent/interfered due to system property unexpectedly. + * This helper class backs up the old properties when invoking [override] and restore the old + * properties in [close] to avoid flaky testing. + */ +class SystemProperty(overrides: Map = mapOf()) : AutoCloseable { + private val oldProperties = mutableMapOf() + + constructor(key: String, value: String?) : this(mapOf(key to value)) + + init { + override(overrides) + } + + fun override(key: String, value: String?) = override(mapOf(key to value)) + + fun override(overrides: Map) { + // back up system properties for the overrides + for (key in overrides.keys) { + // only back up the oldest property + if (!oldProperties.containsKey(key)) { + oldProperties[key] = System.getProperty(key) + } + } + overrides.overrideProperties() + } + + override fun close() { + // restore the backed up properties + oldProperties.overrideProperties() + oldProperties.clear() + } + + private fun Map.overrideProperties() { + for ((key, value) in this) { + if (value != null) { + System.setProperty(key, value) + } else { + System.clearProperty(key) + } + } + } +}