From 90e095dbe372f29823ad4788c0cc2d781ae3bb24 Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Wed, 6 Apr 2022 17:30:27 +0800 Subject: [PATCH 01/11] Fix LaunchAnyWhere in AppRestrictionsFragment If the intent's package equals to the app's package, this intent will be allowed to startActivityForResult. But this check is unsafe, because if the component of this intent is set, the package field will just be ignored. So if we set the component to any activity we like and set package to the app's package, it will pass the assertSafeToStartCustomActivity check and now we can launch anywhere. Bug: 223578534 Test: robotest and manual verify Change-Id: I40496105bae313fe5cff2a36dfe329c1e2b5bbe4 --- src/com/android/settings/users/AppRestrictionsFragment.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index c67a687d180..db7612f7f10 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -661,10 +661,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen } private void assertSafeToStartCustomActivity(Intent intent) { - // Activity can be started if it belongs to the same app - if (intent.getPackage() != null && intent.getPackage().equals(packageName)) { - return; - } + EventLog.writeEvent(0x534e4554, "223578534", -1 /* UID */, ""); ResolveInfo resolveInfo = mPackageManager.resolveActivity( intent, PackageManager.MATCH_DEFAULT_ONLY); From 257ebe4a5e8a9c21abbad642282b08476c1ebb1b Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Mon, 11 Apr 2022 14:39:38 +0800 Subject: [PATCH 02/11] Index the gesture options in System Navigation Settings page Fixes: 167536360 Test: manual verify & robotest Change-Id: Ie1a24a7206153dc1405f8a28369a6bade11ddd39 --- .../SystemNavigationGestureSettings.java | 35 +++++++++++++++++++ .../SystemNavigationGestureSettingsTest.java | 35 +++++++++++++++++-- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java index 7f0d7c338d0..08b043e2260 100644 --- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java +++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.om.IOverlayManager; import android.content.om.OverlayInfo; +import android.content.res.Resources; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; @@ -49,6 +50,7 @@ import com.android.settings.support.actionbar.HelpResourceProvider; import com.android.settings.utils.CandidateInfoExtra; import com.android.settings.widget.RadioButtonPickerFragment; import com.android.settingslib.search.SearchIndexable; +import com.android.settingslib.search.SearchIndexableRaw; import com.android.settingslib.widget.CandidateInfo; import com.android.settingslib.widget.IllustrationPreference; import com.android.settingslib.widget.SelectorWithWidgetPreference; @@ -320,6 +322,39 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i protected boolean isPageSearchEnabled(Context context) { return SystemNavigationPreferenceController.isGestureAvailable(context); } + + @Override + public List getRawDataToIndex(Context context, + boolean enabled) { + final Resources res = context.getResources(); + final List result = new ArrayList<>(); + + if (SystemNavigationPreferenceController.isOverlayPackageAvailable(context, + NAV_BAR_MODE_GESTURAL_OVERLAY)) { + SearchIndexableRaw data = new SearchIndexableRaw(context); + data.title = res.getString(R.string.edge_to_edge_navigation_title); + data.key = KEY_SYSTEM_NAV_GESTURAL; + result.add(data); + } + + if (SystemNavigationPreferenceController.isOverlayPackageAvailable(context, + NAV_BAR_MODE_2BUTTON_OVERLAY)) { + SearchIndexableRaw data = new SearchIndexableRaw(context); + data.title = res.getString(R.string.swipe_up_to_switch_apps_title); + data.key = KEY_SYSTEM_NAV_2BUTTONS; + result.add(data); + } + + if (SystemNavigationPreferenceController.isOverlayPackageAvailable(context, + NAV_BAR_MODE_3BUTTON_OVERLAY)) { + SearchIndexableRaw data = new SearchIndexableRaw(context); + data.title = res.getString(R.string.legacy_navigation_title); + data.key = KEY_SYSTEM_NAV_3BUTTONS; + result.add(data); + } + + return result; + } }; // From HelpResourceProvider diff --git a/tests/robotests/src/com/android/settings/gestures/SystemNavigationGestureSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/SystemNavigationGestureSettingsTest.java index 78424da6e0e..e76157071a5 100644 --- a/tests/robotests/src/com/android/settings/gestures/SystemNavigationGestureSettingsTest.java +++ b/tests/robotests/src/com/android/settings/gestures/SystemNavigationGestureSettingsTest.java @@ -34,6 +34,7 @@ import static junit.framework.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -41,10 +42,14 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.om.IOverlayManager; import android.content.om.OverlayInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.provider.SearchIndexableResource; import com.android.internal.R; import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settingslib.search.SearchIndexableRaw; import org.junit.Before; import org.junit.Test; @@ -67,6 +72,8 @@ public class SystemNavigationGestureSettingsTest { @Mock private IOverlayManager mOverlayManager; @Mock + private PackageManager mPackageManager; + @Mock private OverlayInfo mOverlayInfoEnabled; @Mock private OverlayInfo mOverlayInfoDisabled; @@ -75,16 +82,17 @@ public class SystemNavigationGestureSettingsTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + mContext = spy(RuntimeEnvironment.application); mSettings = new SystemNavigationGestureSettings(); when(mOverlayInfoDisabled.isEnabled()).thenReturn(false); when(mOverlayInfoEnabled.isEnabled()).thenReturn(true); when(mOverlayManager.getOverlayInfo(any(), anyInt())).thenReturn(mOverlayInfoDisabled); + when(mContext.getPackageManager()).thenReturn(mPackageManager); } @Test - public void testSearchIndexProvider_shouldIndexResource() { + public void searchIndexProvider_shouldIndexResource() { final List indexRes = SystemNavigationGestureSettings.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex( RuntimeEnvironment.application, true /* enabled */); @@ -93,6 +101,29 @@ public class SystemNavigationGestureSettingsTest { assertThat(indexRes.get(0).xmlResId).isEqualTo(mSettings.getPreferenceScreenResId()); } + @Test + public void searchIndexProvider_gesturePackageExist_shouldBeIndexed() + throws NameNotFoundException { + PackageInfo info = new PackageInfo(); + when(mPackageManager.getPackageInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, 0)) + .thenReturn(info); + + final List indexRaws = + SystemNavigationGestureSettings.SEARCH_INDEX_DATA_PROVIDER + .getRawDataToIndex(mContext, true /* enabled */); + + assertThat(indexRaws).isNotEmpty(); + } + + @Test + public void searchIndexProvider_noNavigationPackageExist_shouldReturnEmpty() { + final List indexRaws = + SystemNavigationGestureSettings.SEARCH_INDEX_DATA_PROVIDER + .getRawDataToIndex(mContext, true /* enabled */); + + assertThat(indexRaws).isEmpty(); + } + @Test public void testGetCurrentSystemNavigationMode() { SettingsShadowResources.overrideResource( From f8739eda93c6c65fa031116d2cb79d1db6c56a08 Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Mon, 11 Apr 2022 21:50:01 +0800 Subject: [PATCH 03/11] Fix "Factory Reset" fails to reset the device when device didn't support PersistentDataBlockManager, Settings will crash at get PersistentDataBlock service. So we need to check if device support PersistentDataBlockManager before call getService. Fixes: 196634851 Test: manual verify on aosp build. Change-Id: I5afba5df8c4831499478490b442c0fcf367d23ae --- src/com/android/settings/MainClearConfirm.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/com/android/settings/MainClearConfirm.java b/src/com/android/settings/MainClearConfirm.java index c45a907c632..9208c4fbf6a 100644 --- a/src/com/android/settings/MainClearConfirm.java +++ b/src/com/android/settings/MainClearConfirm.java @@ -31,6 +31,7 @@ import android.content.pm.ActivityInfo; import android.graphics.Color; import android.os.AsyncTask; import android.os.Bundle; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.service.oemlock.OemLockManager; @@ -67,6 +68,8 @@ import com.google.android.setupdesign.GlifLayout; public class MainClearConfirm extends InstrumentedFragment { private static final String TAG = "MainClearConfirm"; + private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; + @VisibleForTesting View mContentView; private boolean mEraseSdCard; @VisibleForTesting boolean mEraseEsims; @@ -83,6 +86,11 @@ public class MainClearConfirm extends InstrumentedFragment { return; } + // pre-flight check hardware support PersistentDataBlockManager + if (SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("")) { + return; + } + final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager) getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); From 87bb1b34c5e9e5160a4e3880d174e2611526d7d6 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 12 Apr 2022 16:20:01 -0400 Subject: [PATCH 04/11] Add missing extra when launching apps As promised in the api docs Test: AbstractZenModeAutomaticRulePreferenceControllerTest, view schedule DND page Fixes: 221423986 Change-Id: I7ddc1b112950da225afa2ba13ebf5df481922177 --- ...ModeAutomaticRulePreferenceController.java | 5 ++++- .../zen/ZenModeRuleSettingsBase.java | 11 +++++++--- ...AutomaticRulePreferenceControllerTest.java | 21 +++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java b/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java index 701abbb0b65..cce715de524 100644 --- a/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java +++ b/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java @@ -16,6 +16,8 @@ package com.android.settings.notification.zen; +import static android.app.NotificationManager.EXTRA_AUTOMATIC_RULE_ID; + import android.app.AutomaticZenRule; import android.app.NotificationManager; import android.app.settings.SettingsEnums; @@ -79,7 +81,8 @@ abstract public class AbstractZenModeAutomaticRulePreferenceController extends ComponentName configurationActivity, String ruleId) { final Intent intent = new Intent() .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId); + .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId) + .putExtra(EXTRA_AUTOMATIC_RULE_ID, ruleId); if (configurationActivity != null) { intent.setComponent(configurationActivity); } else { diff --git a/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java index 5ce8b48b27c..e155093996a 100644 --- a/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java +++ b/src/com/android/settings/notification/zen/ZenModeRuleSettingsBase.java @@ -16,6 +16,8 @@ package com.android.settings.notification.zen; +import static android.app.NotificationManager.EXTRA_AUTOMATIC_RULE_ID; + import android.app.AutomaticZenRule; import android.app.NotificationManager; import android.content.Context; @@ -72,9 +74,12 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase { mId = intent.getStringExtra(ConditionProviderService.EXTRA_RULE_ID); if (mId == null) { - Log.w(TAG, "rule id is null"); - toastAndFinish(); - return; + mId = intent.getStringExtra(EXTRA_AUTOMATIC_RULE_ID); + if (mId == null) { + Log.w(TAG, "rule id is null"); + toastAndFinish(); + return; + } } if (DEBUG) Log.d(TAG, "mId=" + mId); diff --git a/tests/robotests/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceControllerTest.java index ae6e1d0c9c6..1c247a6b8f6 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceControllerTest.java @@ -16,6 +16,9 @@ package com.android.settings.notification.zen; +import static android.app.NotificationManager.EXTRA_AUTOMATIC_RULE_ID; +import static android.service.notification.ConditionProviderService.EXTRA_RULE_ID; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; @@ -24,6 +27,7 @@ import android.app.AutomaticZenRule; import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.net.Uri; @@ -166,4 +170,21 @@ public class AbstractZenModeAutomaticRulePreferenceControllerTest { assertThat(actual).isEqualTo(new ComponentName(mContext.getPackageName(), "activity")); } + + @Test + public void testGetRuleIntent() throws Exception { + AutomaticZenRule rule = new AutomaticZenRule("name", null, + new ComponentName(mContext.getPackageName(), "test"), Uri.EMPTY, + new ZenPolicy(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + rule.setPackageName(mContext.getPackageName()); + + when(mPm.getPackageUid(null, 0)).thenReturn(-1); + when(mPm.getPackageUid(mContext.getPackageName(), 0)).thenReturn(1); + + Intent intent = AbstractZenModeAutomaticRulePreferenceController + .getRuleIntent(null, rule.getConfigurationActivity(), "id"); + + assertThat("id").isEqualTo(intent.getStringExtra(EXTRA_RULE_ID)); + assertThat("id").isEqualTo(intent.getStringExtra(EXTRA_AUTOMATIC_RULE_ID)); + } } \ No newline at end of file From 37edbd33b8197eabfd9e856c946ca4ae7eda7e13 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Wed, 13 Apr 2022 05:04:47 +0800 Subject: [PATCH 05/11] Remove redundant WiFi tethering listener - WiFi tethering settings currently listeners two sets of WiFi tethering state from framework at the same time, There is a timing issue when the deprecated callback is sent earlier than the available callback, it will cause WiFi tethering settings to start tethering twice in a short period of time, causing the WiFi framework to respond with a startup error. - Remove redundant obsolete WiFi tethering state listener to avoid WiFi tethering startup error. Bug: 227719584 Test: manual test make RunSettingsRoboTests ROBOTEST_FILTER=WifiTetherSettingsTest Change-Id: I59cb5222acb763f630fb621250d7740a240bbd92 --- .../settings/wifi/tether/WifiTetherSettings.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 4f2f3c07159..93d267b74a1 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -16,7 +16,6 @@ package com.android.settings.wifi.tether; -import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION; import android.app.settings.SettingsEnums; @@ -32,7 +31,6 @@ import android.util.FeatureFlagUtils; import android.util.Log; import androidx.annotation.VisibleForTesting; -import androidx.preference.PreferenceGroup; import com.android.settings.R; import com.android.settings.SettingsActivity; @@ -79,8 +77,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment TetherChangeReceiver mTetherChangeReceiver; static { - TETHER_STATE_CHANGE_FILTER = new IntentFilter(ACTION_TETHER_STATE_CHANGED); - TETHER_STATE_CHANGE_FILTER.addAction(WIFI_AP_STATE_CHANGED_ACTION); + TETHER_STATE_CHANGE_FILTER = new IntentFilter(WIFI_AP_STATE_CHANGED_ACTION); } public WifiTetherSettings() { @@ -269,12 +266,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment String action = intent.getAction(); Log.d(TAG, "updating display config due to receiving broadcast action " + action); updateDisplayWithNewConfig(); - if (action.equals(ACTION_TETHER_STATE_CHANGED)) { - if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED - && mRestartWifiApAfterConfigChange) { - startTether(); - } - } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) { + if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) { int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0); if (state == WifiManager.WIFI_AP_STATE_DISABLED && mRestartWifiApAfterConfigChange) { From 09a2a49bd3e6d61c9732ec1dadfe7f7ab97eb463 Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Wed, 6 Apr 2022 17:45:59 +0800 Subject: [PATCH 06/11] =?UTF-8?q?Fix=20that=20device=20isn't=20responding?= =?UTF-8?q?=20for=20a=20while=20when=20resetting=20all=20settings=20on=20?= =?UTF-8?q?=E2=80=9CDisplay=20size=20and=20text=E2=80=9D=20page.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Goal: Probably has the race condition issue between "Bold text" and the other features including "Display Size", “Font Size” if they would be enabled at the same time, so our workaround is that the “Bold text” would be reset first and then do the remaining to avoid flickering problem. Bug: 223747686 Bug: 220082104 Bug: 220070773 Test: make RunSettingsRoboTests ROBOTEST_FILTER=TextReadingPreferenceFragmentTest Change-Id: If1425fe2579bec8dded69680ac73fbfb03c37321 --- .../TextReadingPreferenceFragment.java | 54 ++++++++- .../TextReadingPreferenceFragmentTest.java | 107 ++++++++++++++++++ 2 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentTest.java diff --git a/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java b/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java index da0287677f6..11485844127 100644 --- a/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java +++ b/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java @@ -22,6 +22,7 @@ import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; +import android.os.Bundle; import androidx.appcompat.app.AlertDialog; @@ -32,6 +33,8 @@ import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.search.SearchIndexable; +import com.google.common.annotations.VisibleForTesting; + import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -49,6 +52,26 @@ public class TextReadingPreferenceFragment extends DashboardFragment { private static final String RESET_KEY = "reset"; private static final String BOLD_TEXT_KEY = "toggle_force_bold_text"; private static final String HIGHT_TEXT_CONTRAST_KEY = "toggle_high_text_contrast_preference"; + private static final String NEED_RESET_SETTINGS = "need_reset_settings"; + private FontWeightAdjustmentPreferenceController mFontWeightAdjustmentController; + + @VisibleForTesting + List mResetStateListeners; + + @VisibleForTesting + boolean mNeedResetSettings; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mNeedResetSettings = false; + mResetStateListeners = getResetStateListeners(); + + if (icicle != null && icicle.getBoolean(NEED_RESET_SETTINGS)) { + mResetStateListeners.forEach(ResetStateListener::resetState); + } + } @Override protected int getPreferenceScreenResId() { @@ -69,7 +92,7 @@ public class TextReadingPreferenceFragment extends DashboardFragment { protected List createPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); final FontSizeData fontSizeData = new FontSizeData(context); - final DisplaySizeData displaySizeData = new DisplaySizeData(context); + final DisplaySizeData displaySizeData = createDisplaySizeData(context); final TextReadingPreviewController previewController = new TextReadingPreviewController( context, PREVIEW_KEY, fontSizeData, displaySizeData); @@ -85,9 +108,9 @@ public class TextReadingPreferenceFragment extends DashboardFragment { displaySizeController.setInteractionListener(previewController); controllers.add(displaySizeController); - final FontWeightAdjustmentPreferenceController fontWeightController = + mFontWeightAdjustmentController = new FontWeightAdjustmentPreferenceController(context, BOLD_TEXT_KEY); - controllers.add(fontWeightController); + controllers.add(mFontWeightAdjustmentController); final HighTextContrastPreferenceController highTextContrastController = new HighTextContrastPreferenceController(context, HIGHT_TEXT_CONTRAST_KEY); @@ -126,12 +149,35 @@ public class TextReadingPreferenceFragment extends DashboardFragment { return super.getDialogMetricsCategory(dialogId); } + @Override + public void onSaveInstanceState(Bundle outState) { + if (mNeedResetSettings) { + outState.putBoolean(NEED_RESET_SETTINGS, true); + } + } + + @VisibleForTesting + DisplaySizeData createDisplaySizeData(Context context) { + return new DisplaySizeData(context); + } + private void onPositiveButtonClicked(DialogInterface dialog, int which) { // To avoid showing the dialog again, probably the onDetach() of SettingsDialogFragment // was interrupted by unexpectedly recreating the activity. removeDialog(DialogEnums.DIALOG_RESET_SETTINGS); - getResetStateListeners().forEach(ResetStateListener::resetState); + if (mFontWeightAdjustmentController.isChecked()) { + // TODO(b/228956791): Consider replacing or removing it once the root cause is + // clarified and the better method is available. + // Probably has the race condition issue between "Bold text" and the other features + // including "Display Size", “Font Size” if they would be enabled at the same time, + // so our workaround is that the “Bold text” would be reset first and then do the + // remaining to avoid flickering problem. + mNeedResetSettings = true; + mFontWeightAdjustmentController.resetState(); + } else { + mResetStateListeners.forEach(ResetStateListener::resetState); + } } private List getResetStateListeners() { diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentTest.java new file mode 100644 index 00000000000..1793cc28984 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 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.accessibility; + +import static com.android.settings.accessibility.FontWeightAdjustmentPreferenceController.BOLD_TEXT_ADJUSTMENT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.DialogInterface; +import android.provider.Settings; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; +import androidx.preference.PreferenceManager; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums; +import com.android.settings.accessibility.TextReadingResetController.ResetStateListener; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Tests for {@link TextReadingPreferenceFragment}. + */ +@RunWith(RobolectricTestRunner.class) +public class TextReadingPreferenceFragmentTest { + private TextReadingPreferenceFragment mFragment; + private Context mContext = ApplicationProvider.getApplicationContext(); + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private PreferenceManager mPreferenceManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext.setTheme(R.style.Theme_AppCompat); + + mFragment = spy(new TextReadingPreferenceFragment()); + when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager); + when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext); + when(mFragment.getContext()).thenReturn(mContext); + when(mFragment.getActivity()).thenReturn(Robolectric.setupActivity(FragmentActivity.class)); + + // Avoid a NPE is happened in ShadowWindowManagerGlobal + doReturn(mock(DisplaySizeData.class)).when(mFragment).createDisplaySizeData(mContext); + mFragment.createPreferenceControllers(mContext); + } + + @Test + public void onDialogPositiveButtonClicked_boldTextEnabled_needResetSettings() { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.FONT_WEIGHT_ADJUSTMENT, BOLD_TEXT_ADJUSTMENT); + final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog( + DialogEnums.DIALOG_RESET_SETTINGS); + dialog.show(); + + dialog.getButton(DialogInterface.BUTTON_POSITIVE).callOnClick(); + + assertThat(mFragment.mNeedResetSettings).isTrue(); + } + + @Test + public void onDialogPositiveButtonClicked_boldTextDisabled_resetAllListeners() { + final ResetStateListener listener1 = mock(ResetStateListener.class); + final ResetStateListener listener2 = mock(ResetStateListener.class); + mFragment.mResetStateListeners = new ArrayList<>(Arrays.asList(listener1, listener2)); + final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog( + DialogEnums.DIALOG_RESET_SETTINGS); + dialog.show(); + + dialog.getButton(DialogInterface.BUTTON_POSITIVE).callOnClick(); + + verify(listener1).resetState(); + verify(listener2).resetState(); + } +} From 42d5979f6981a7e6b3e1b191e465f94faeda2d0f Mon Sep 17 00:00:00 2001 From: ykhung Date: Wed, 13 Apr 2022 11:18:18 +0800 Subject: [PATCH 07/11] [Security] Verify the permission first before querying installed apps Verify the target app "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" permission first before querying installed apps information to avoid the exposed the installed apps information and improve the performance by applying the early return flow. Bug: 227753723 Test: make RunSettingsRoboTests Change-Id: Ib5826b8082f4bf14f93d5ff4d2d332dc395bf9a8 --- .../RequestIgnoreBatteryOptimizations.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java b/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java index ee4609001bb..9ef8c8ce747 100644 --- a/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java +++ b/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java @@ -69,15 +69,6 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity implements return; } - ApplicationInfo ai; - try { - ai = getPackageManager().getApplicationInfo(mPackageName, 0); - } catch (PackageManager.NameNotFoundException e) { - debugLog("Requested package doesn't exist: " + mPackageName); - finish(); - return; - } - if (getPackageManager().checkPermission( Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, mPackageName) != PackageManager.PERMISSION_GRANTED) { @@ -87,6 +78,15 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity implements return; } + ApplicationInfo ai; + try { + ai = getPackageManager().getApplicationInfo(mPackageName, 0); + } catch (PackageManager.NameNotFoundException e) { + debugLog("Requested package doesn't exist: " + mPackageName); + finish(); + return; + } + final AlertController.AlertParams p = mAlertParams; final CharSequence appLabel = ai.loadSafeLabel(getPackageManager(), PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX, PackageItemInfo.SAFE_LABEL_FLAG_TRIM From 25dc3817a9dcb758dcc26f934d52cca5c4732d0f Mon Sep 17 00:00:00 2001 From: tom hsu Date: Wed, 13 Apr 2022 14:42:21 +0800 Subject: [PATCH 08/11] [Panlingual] Fix settings's crash. - fix crash due to no listview. Bug: 229044820 Test: local Change-Id: Ic3a15f9cfd89ff24ce1d764417de4686e10d3008 --- .../localepicker/AppLocalePickerActivity.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/com/android/settings/localepicker/AppLocalePickerActivity.java b/src/com/android/settings/localepicker/AppLocalePickerActivity.java index d3c855e658a..4700f3f5383 100644 --- a/src/com/android/settings/localepicker/AppLocalePickerActivity.java +++ b/src/com/android/settings/localepicker/AppLocalePickerActivity.java @@ -28,6 +28,7 @@ import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.FrameLayout; +import android.widget.ListView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.LocalePickerWithRegion; @@ -49,6 +50,7 @@ public class AppLocalePickerActivity extends SettingsBaseActivity private LocalePickerWithRegion mLocalePickerWithRegion; private AppLocaleDetails mAppLocaleDetails; private Context mContextAsUser; + private View mAppLocaleDetailContainer; @Override public void onCreate(Bundle savedInstanceState) { @@ -82,7 +84,7 @@ public class AppLocalePickerActivity extends SettingsBaseActivity false /* translate only */, mPackageName); mAppLocaleDetails = AppLocaleDetails.newInstance(mPackageName); - + mAppLocaleDetailContainer = launchAppLocaleDetailsPage(); // Launch Locale picker part. launchLocalePickerPage(); } @@ -134,12 +136,14 @@ public class AppLocalePickerActivity extends SettingsBaseActivity fragmentManager.registerFragmentLifecycleCallbacks( new android.app.FragmentManager.FragmentLifecycleCallbacks() { @Override - public void onFragmentResumed( + public void onFragmentViewCreated( android.app.FragmentManager fm, - android.app.Fragment f) { - super.onFragmentResumed(fm, f); - mLocalePickerWithRegion.getListView() - .addHeaderView(launchAppLocaleDetailsPage()); + android.app.Fragment f, View v, Bundle s) { + super.onFragmentViewCreated(fm, f, v, s); + ListView listView = (ListView) v.findViewById(android.R.id.list); + if (listView != null) { + listView.addHeaderView(mAppLocaleDetailContainer); + } } }, true); fragmentManager.beginTransaction() From d2ce1d9194d49e0678ae55b1ff51c70720fface3 Mon Sep 17 00:00:00 2001 From: Wesley Wang Date: Tue, 12 Apr 2022 21:00:23 +0800 Subject: [PATCH 09/11] Unchecked battery saver switch once it's 1st launch - Add low battery warning ack check to onSwitchChanged(), reset the switch to off once it's 1st time launch, the switch should be enable by battery saver change event at 1st instead of enable directly, will not check this state anymore after 1st launch Bug: 227725656 Test: make RunSettingsRoboTests Change-Id: I79f502f567fae5dd10166d8b24fd50d59c261001 --- ...atterySaverButtonPreferenceController.java | 6 +++++ ...rySaverButtonPreferenceControllerTest.java | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceController.java index da0f2e2fbe5..ee3f54f70e0 100644 --- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceController.java @@ -22,6 +22,7 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; +import android.provider.Settings; import android.provider.SettingsSlicesContract; import android.widget.Switch; @@ -101,6 +102,11 @@ public class BatterySaverButtonPreferenceController extends @Override public void onSwitchChanged(Switch switchView, boolean isChecked) { + // Cancel preference's check state once it's first time launch + if (isChecked && (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) == 0)) { + mPreference.setChecked(false); + } setChecked(isChecked); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java index 594de6a6e6a..881a18d6baf 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; import android.os.PowerManager; +import android.provider.Settings; import android.provider.SettingsSlicesContract; import androidx.preference.PreferenceScreen; @@ -75,6 +76,24 @@ public class BatterySaverButtonPreferenceControllerTest { .isEqualTo(SettingsSlicesContract.AUTHORITY); } + @Test + public void onSwitchChanged_isCheckedButNotAcked_preferenceIsUnchecked() { + setLowPowerWarningAcked(/* acked= */ 0); + + mController.onSwitchChanged(/* switchView= */ null, /* isChecked= */ true); + + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void onSwitchChanged_isCheckedAndAcked_setPowerSaveMode() { + setLowPowerWarningAcked(/* acked= */ 1); + + mController.onSwitchChanged(/* switchView= */ null, /* isChecked= */ true); + + verify(mPowerManager).setPowerSaveModeEnabled(true); + } + @Test public void updateState_lowPowerOn_preferenceIsChecked() { when(mPowerManager.isPowerSaveMode()).thenReturn(true); @@ -126,4 +145,12 @@ public class BatterySaverButtonPreferenceControllerTest { public void isPublicSlice_returnsTrue() { assertThat(mController.isPublicSlice()).isTrue(); } + + // 0 means not acked, 1 means acked. + private void setLowPowerWarningAcked(int acked) { + Settings.Secure.putInt( + mContext.getContentResolver(), + Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, + acked); + } } From 9ee9787dd393a922262c21a37c98c17efc417ac9 Mon Sep 17 00:00:00 2001 From: Calvin Pan Date: Wed, 13 Apr 2022 09:12:16 +0000 Subject: [PATCH 10/11] Remove subtitle of system default Bug: 226894987 Test: manual Change-Id: Iaa5e680567f5bb1d52f9a3ea911f1b573ba5b238 --- res/values/strings.xml | 2 +- .../settings/applications/appinfo/AppLocaleDetails.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 2e8bcc01822..553a1752350 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -519,7 +519,7 @@ System language - System default - %1$s + System default Language selection for this app isn\u2019t available from Settings. diff --git a/src/com/android/settings/applications/appinfo/AppLocaleDetails.java b/src/com/android/settings/applications/appinfo/AppLocaleDetails.java index 70383d17f24..4e5eb1d1abd 100644 --- a/src/com/android/settings/applications/appinfo/AppLocaleDetails.java +++ b/src/com/android/settings/applications/appinfo/AppLocaleDetails.java @@ -276,9 +276,7 @@ public class AppLocaleDetails extends SettingsPreferenceFragment { final Context contextAsUser = context.createContextAsUser(userHandle, 0); Locale appLocale = getAppDefaultLocale(contextAsUser, entry.info.packageName); if (appLocale == null) { - Locale systemLocale = Locale.getDefault(); - return context.getString(R.string.preference_of_system_locale_summary, - systemLocale.getDisplayName(systemLocale)); + return context.getString(R.string.preference_of_system_locale_summary); } else { return appLocale.getDisplayName(appLocale); } From ddbff186f3d8468c3ab41ec3905862d0d23d5d9e Mon Sep 17 00:00:00 2001 From: tom hsu Date: Wed, 13 Apr 2022 19:16:03 +0800 Subject: [PATCH 11/11] [Panlingual] Make error message generic. Bug: 226032712 Test: local Change-Id: Ibfdc3d5807df4f8e5f1aa3fd37a8f361624c4129 --- res/values/strings.xml | 3 --- .../settings/applications/appinfo/AppLocaleDetails.java | 8 ++------ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 863bd8d9d75..ebed05b5926 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -524,9 +524,6 @@ Language selection for this app isn\u2019t available from Settings. - - You can\u2019t select a language for this app from Settings. - Remove selected language? diff --git a/src/com/android/settings/applications/appinfo/AppLocaleDetails.java b/src/com/android/settings/applications/appinfo/AppLocaleDetails.java index 70383d17f24..59705f4ac49 100644 --- a/src/com/android/settings/applications/appinfo/AppLocaleDetails.java +++ b/src/com/android/settings/applications/appinfo/AppLocaleDetails.java @@ -208,13 +208,9 @@ public class AppLocaleDetails extends SettingsPreferenceFragment { LocaleList packageLocaleList = getPackageLocales(); String[] assetLocaleList = getAssetLocales(); // TODO add apended url string, "Learn more", to these both sentenses. - if (packageLocaleList == null && assetLocaleList.length == 0) { - // There is no locale info from PackageManager amd AssetManager. + if ((packageLocaleList != null && packageLocaleList.isEmpty()) + || (packageLocaleList == null && assetLocaleList.length == 0)) { return R.string.desc_no_available_supported_locale; - } else if (packageLocaleList != null && packageLocaleList.isEmpty()) { - // LocaleConfig is empty, and this means only allow user modify language - // by the application. - return R.string.desc_disallow_locale_change_in_settings; } return -1; }