diff --git a/res/values/strings.xml b/res/values/strings.xml index 8625fe332d3..18836a01475 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6434,6 +6434,7 @@ + @@ -10828,14 +10829,26 @@ Capture system heap dump + + + Memory Tagging Extension + + Memory Tagging Extension (MTE) makes it easier to find memory safety issues in your app and make native code in it more secure. + Turning on MTE might cause slower device performance. + Learn more about MTE + Enable MTE until you turn it off + + You\u0027ll need to restart your device to turn on MTE. + + You\u0027ll need to restart your device to turn off MTE. - Reboot with MTE - System will reboot and allow to experiment with Memory Tagging Extension (MTE). MTE may negatively impact system performance and stability. Will be reset on next subsequent reboot. + Enable MTE for a single session + System will restart and allow to experiment with Memory Tagging Extension (MTE). MTE may negatively impact system performance and stability. Will be reset on next subsequent reboot. - Try MTE for a single boot for app development + Restart for a single session with MTE enabled - MTE is enabled through Advanced memory protection + MTE is already enabled Capturing system heap dump diff --git a/res/xml/development_memtag_page.xml b/res/xml/development_memtag_page.xml new file mode 100644 index 00000000000..e230821a3da --- /dev/null +++ b/res/xml/development_memtag_page.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index 82d7e43e5d1..e8bd9f01229 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -45,8 +45,9 @@ android:title="@string/capture_system_heap_dump_title" /> + android:key="development_memtag_page" + android:title="@string/development_memtag_page_title" + android:fragment="com.android.settings.development.DevelopmentMemtagPage" /> + mContext.startActivity( + HelpUtils.getHelpIntent( + mContext, helpUrl, /* backupContext= */ ""))); + prefFooter.setLearnMoreText(mContext.getString(R.string.development_memtag_learn_more)); + } + } +} diff --git a/src/com/android/settings/development/DevelopmentMemtagPage.java b/src/com/android/settings/development/DevelopmentMemtagPage.java new file mode 100644 index 00000000000..df983f3d4ed --- /dev/null +++ b/src/com/android/settings/development/DevelopmentMemtagPage.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 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; + +import android.app.settings.SettingsEnums; +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +@SearchIndexable +public class DevelopmentMemtagPage extends DashboardFragment { + private static final String TAG = "DevelopmentMemtagPage"; + + @Override + public int getMetricsCategory() { + return SettingsEnums.SETTINGS_DEVELOPMENT_MEMTAG_CATEGORY; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + + use(RebootWithMtePreferenceController.class).setFragment(this); + use(DevelopmentMemtagPreferenceController.class).setFragment(this); + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.development_memtag_page; + } + + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.development_memtag_page); +} diff --git a/src/com/android/settings/development/DevelopmentMemtagPagePreferenceController.java b/src/com/android/settings/development/DevelopmentMemtagPagePreferenceController.java new file mode 100644 index 00000000000..240079b3255 --- /dev/null +++ b/src/com/android/settings/development/DevelopmentMemtagPagePreferenceController.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 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; + +import android.content.Context; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.security.MemtagHelper; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +public class DevelopmentMemtagPagePreferenceController extends DeveloperOptionsPreferenceController + implements PreferenceControllerMixin { + private static final String KEY_DEVELOPMENT_MEMTAG_PAGE = "development_memtag_page"; + + public DevelopmentMemtagPagePreferenceController( + Context context, DevelopmentSettingsDashboardFragment fragment) { + super(context); + } + + @Override + public boolean isAvailable() { + return android.os.SystemProperties.getBoolean("ro.arm64.memtag.bootctl_supported", false); + } + + @Override + public String getPreferenceKey() { + return KEY_DEVELOPMENT_MEMTAG_PAGE; + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + super.onDeveloperOptionsSwitchDisabled(); + MemtagHelper.setChecked(false); + } +} diff --git a/src/com/android/settings/development/DevelopmentMemtagPreferenceController.java b/src/com/android/settings/development/DevelopmentMemtagPreferenceController.java new file mode 100644 index 00000000000..751ccbae83d --- /dev/null +++ b/src/com/android/settings/development/DevelopmentMemtagPreferenceController.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2023 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; + +import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + +import android.content.Context; +import android.os.SystemProperties; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.TogglePreferenceController; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.security.MemtagHelper; +import com.android.settings.security.MemtagRebootDialog; +import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.RestrictedSwitchPreference; +import com.android.settingslib.development.DevelopmentSettingsEnabler; + +public class DevelopmentMemtagPreferenceController extends TogglePreferenceController { + private Preference mPreference; + private DashboardFragment mFragment; + + public DevelopmentMemtagPreferenceController(Context context, String key) { + super(context, key); + } + + public void setFragment(DashboardFragment fragment) { + mFragment = fragment; + } + + @Override + public int getAvailabilityStatus() { + return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext) + && SystemProperties.getBoolean("ro.arm64.memtag.bootctl_supported", false) + ? BasePreferenceController.AVAILABLE + : BasePreferenceController.UNSUPPORTED_ON_DEVICE; + } + + @Override + public boolean isChecked() { + return MemtagHelper.isChecked(); + } + + @Override + public boolean setChecked(boolean isChecked) { + MemtagHelper.setChecked(isChecked); + if (mPreference != null) { + refreshSummary(mPreference); + } + if (isChecked != MemtagHelper.isOn()) { + int msg = + isChecked + ? R.string.development_memtag_reboot_message_on + : R.string.development_memtag_reboot_message_off; + MemtagRebootDialog.show(mContext, mFragment, msg); + } + mFragment.forceUpdatePreferences(); + return true; + } + + @Override + public int getSliceHighlightMenuRes() { + return R.string.menu_key_system; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + refreshSummary(mPreference); + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfMteIsDisabled(mContext); + if (admin != null) { + // Make sure this is disabled even if the user directly goes to this + // page via the android.settings.ADVANCED_MEMORY_PROTECTION_SETTINGS intent. + ((RestrictedSwitchPreference) preference).setDisabledByAdmin(admin); + } + refreshSummary(preference); + } + + @Override + public CharSequence getSummary() { + return mContext.getResources().getString(MemtagHelper.getSummary()); + } +} diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 8ff23365a5a..b8139c987b8 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -544,7 +544,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new BugReportPreferenceController(context)); controllers.add(new BugReportHandlerPreferenceController(context)); controllers.add(new SystemServerHeapDumpPreferenceController(context)); - controllers.add(new RebootWithMtePreferenceController(context, fragment)); + controllers.add(new DevelopmentMemtagPagePreferenceController(context, fragment)); controllers.add(new LocalBackupPasswordPreferenceController(context)); controllers.add(new StayAwakePreferenceController(context, lifecycle)); controllers.add(new HdcpCheckingPreferenceController(context)); diff --git a/src/com/android/settings/development/RebootWithMtePreferenceController.java b/src/com/android/settings/development/RebootWithMtePreferenceController.java index f7bd7261f4a..76ad2ce3fe8 100644 --- a/src/com/android/settings/development/RebootWithMtePreferenceController.java +++ b/src/com/android/settings/development/RebootWithMtePreferenceController.java @@ -17,32 +17,35 @@ package com.android.settings.development; import android.content.Context; +import android.os.SystemProperties; import android.text.TextUtils; +import androidx.fragment.app.Fragment; import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.Utils; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.security.MemtagHelper; -import com.android.settingslib.development.DeveloperOptionsPreferenceController; +import com.android.settingslib.development.DevelopmentSettingsEnabler; -public class RebootWithMtePreferenceController extends DeveloperOptionsPreferenceController +public class RebootWithMtePreferenceController extends BasePreferenceController implements PreferenceControllerMixin { - private static final String KEY_REBOOT_WITH_MTE = "reboot_with_mte"; - private final DevelopmentSettingsDashboardFragment mFragment; + private Fragment mFragment; - public RebootWithMtePreferenceController( - Context context, DevelopmentSettingsDashboardFragment fragment) { - super(context); - mFragment = fragment; + public RebootWithMtePreferenceController(Context context) { + super(context, KEY_REBOOT_WITH_MTE); } @Override - public boolean isAvailable() { - return android.os.SystemProperties.getBoolean("ro.arm64.memtag.bootctl_supported", false); + public int getAvailabilityStatus() { + return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext) + && SystemProperties.getBoolean("ro.arm64.memtag.bootctl_supported", false) + ? BasePreferenceController.AVAILABLE + : BasePreferenceController.UNSUPPORTED_ON_DEVICE; } @Override @@ -64,6 +67,10 @@ public class RebootWithMtePreferenceController extends DeveloperOptionsPreferenc return KEY_REBOOT_WITH_MTE; } + public void setFragment(Fragment fragment) { + mFragment = fragment; + } + @Override public boolean handlePreferenceTreeClick(Preference preference) { if (Utils.isMonkeyRunning()) { diff --git a/src/com/android/settings/security/MemtagPreferenceController.java b/src/com/android/settings/security/MemtagPreferenceController.java index b5a4be71490..5e224a1ec93 100644 --- a/src/com/android/settings/security/MemtagPreferenceController.java +++ b/src/com/android/settings/security/MemtagPreferenceController.java @@ -1,5 +1,6 @@ /* * 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. @@ -58,7 +59,11 @@ public class MemtagPreferenceController extends TogglePreferenceController { refreshSummary(mPreference); } if (isChecked != MemtagHelper.isOn()) { - MemtagRebootDialog.show(mContext, mFragment, isChecked); + int msg = + isChecked + ? R.string.memtag_reboot_message_on + : R.string.memtag_reboot_message_off; + MemtagRebootDialog.show(mContext, mFragment, msg); } return true; } diff --git a/src/com/android/settings/security/MemtagRebootDialog.java b/src/com/android/settings/security/MemtagRebootDialog.java index 735de8f8058..adedff8a378 100644 --- a/src/com/android/settings/security/MemtagRebootDialog.java +++ b/src/com/android/settings/security/MemtagRebootDialog.java @@ -34,16 +34,16 @@ public class MemtagRebootDialog extends InstrumentedDialogFragment implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener { public static final String TAG = "MemtagRebootDialog"; - private boolean mIsChecked; + private int mMessage; - public MemtagRebootDialog(Context context, boolean isChecked) { - mIsChecked = isChecked; + public MemtagRebootDialog(Context context, int msg) { + mMessage = msg; } - public static void show(Context context, Fragment host, boolean isChecked) { + public static void show(Context context, Fragment host, int msg) { final FragmentManager manager = host.getActivity().getSupportFragmentManager(); if (manager.findFragmentByTag(TAG) == null) { - final MemtagRebootDialog dialog = new MemtagRebootDialog(context, isChecked); + final MemtagRebootDialog dialog = new MemtagRebootDialog(context, msg); dialog.show(manager, TAG); } } @@ -55,11 +55,9 @@ public class MemtagRebootDialog extends InstrumentedDialogFragment @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - int msg = - mIsChecked ? R.string.memtag_reboot_message_on : R.string.memtag_reboot_message_off; return new AlertDialog.Builder(getActivity()) .setTitle(R.string.memtag_reboot_title) - .setMessage(msg) + .setMessage(mMessage) .setPositiveButton(R.string.memtag_reboot_yes, this /* onClickListener */) .setNegativeButton(R.string.memtag_reboot_no, null /* onClickListener */) .create(); diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPagePreferenceControllerTest.java new file mode 100644 index 00000000000..7989682f478 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPagePreferenceControllerTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 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; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.content.Context; +import android.os.SystemProperties; + +import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowSystemProperties; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowRestrictedLockUtilsInternal.class}) +public class DevelopmentMemtagPagePreferenceControllerTest { + private final String mMemtagSupportedProperty = "ro.arm64.memtag.bootctl_supported"; + + private DevelopmentMemtagPagePreferenceController mController; + private Context mContext; + + @Mock private DevelopmentSettingsDashboardFragment mFragment; + private static final String FRAGMENT_TAG = "memtag_page"; + + @Before + public void setUp() { + ShadowSystemProperties.override(mMemtagSupportedProperty, "true"); + + mContext = RuntimeEnvironment.application; + mController = new DevelopmentMemtagPagePreferenceController(mContext, mFragment); + } + + @Test + public void onAvailable_sysPropEnabled() { + SystemProperties.set("ro.arm64.memtag.bootctl_supported", "1"); + assertTrue(mController.isAvailable()); + } + + @Test + public void onAvailable_sysPropDisabled() { + SystemProperties.set("ro.arm64.memtag.bootctl_supported", "0"); + assertFalse(mController.isAvailable()); + } +} diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPageTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPageTest.java new file mode 100644 index 00000000000..39dc48e710b --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPageTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 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; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.settings.SettingsEnums; +import android.content.Context; + +import com.android.settings.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class DevelopmentMemtagPageTest { + private DevelopmentMemtagPage mMemtagPage; + private Context mContext; + + @Before + public void setUp() { + mMemtagPage = new DevelopmentMemtagPage(); + mContext = RuntimeEnvironment.application; + } + + @Test + public void getMetricsCategory_isSETTINGS_MEMTAG_CATEGORY() { + assertThat(mMemtagPage.getMetricsCategory()) + .isEqualTo(SettingsEnums.SETTINGS_MEMTAG_CATEGORY); + } + + @Test + public void getPreferenceScreenResId_isMemtag_page() { + assertThat(mMemtagPage.getPreferenceScreenResId()).isEqualTo(R.xml.development_memtag_page); + } + + @Test + public void SEARCH_INDEX_DATA_PROVIDERgetPreferenceControllers_isNotEmpty() { + assertThat( + DevelopmentMemtagPage.SEARCH_INDEX_DATA_PROVIDER.getPreferenceControllers( + mContext)) + .isNotEmpty(); + } +} diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPreferenceControllerTest.java new file mode 100644 index 00000000000..d4af47040b9 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/DevelopmentMemtagPreferenceControllerTest.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2023 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; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.matcher.RootMatchers.isDialog; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.os.Bundle; + +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentContainerView; +import androidx.test.rule.ActivityTestRule; + +import com.android.settings.R; +import com.android.settings.security.ZygoteShadow; +import com.android.settings.testutils.shadow.ShadowDeviceConfig; +import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; +import com.android.settingslib.RestrictedSwitchPreference; +import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowSystemProperties; + +@RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + ZygoteShadow.class, + ShadowDeviceConfig.class, + ShadowInteractionJankMonitor.class, + ShadowRestrictedLockUtilsInternal.class + }) +public class DevelopmentMemtagPreferenceControllerTest { + private final String mMemtagSupportedProperty = "ro.arm64.memtag.bootctl_supported"; + + @Rule + public ActivityTestRule mActivityTestRule = + new ActivityTestRule<>(TestActivity.class); + + private DevelopmentMemtagPage mMemtagPage; + private DevelopmentMemtagPreferenceController mController; + private Context mContext; + private TestActivity mActivity; + + private static final String FRAGMENT_TAG = "development_memtag_page"; + + @Before + public void setUp() { + ShadowSystemProperties.override(mMemtagSupportedProperty, "true"); + + mContext = RuntimeEnvironment.application; + mMemtagPage = new DevelopmentMemtagPage(); + mActivity = mActivityTestRule.getActivity(); + mActivity + .getSupportFragmentManager() + .beginTransaction() + .add(TestActivity.CONTAINER_VIEW_ID, mMemtagPage) + .commit(); + mController = new DevelopmentMemtagPreferenceController(mContext, FRAGMENT_TAG); + mController.setFragment(mMemtagPage); + } + + @Test + public void getSliceHighlightMenuRes_isMenu_key_security() { + assertThat(mController.getSliceHighlightMenuRes()).isEqualTo(R.string.menu_key_security); + } + + @Test + public void setChecked_isChecked_updatesSummary() { + ZygoteShadow.setSupportsMemoryTagging(true); + mController.setChecked(true); + assertThat(mController.getSummary()) + .isEqualTo(mContext.getResources().getString(R.string.memtag_on)); + } + + @Test + public void setChecked_isUnchecked_updatesSummary() { + ZygoteShadow.setSupportsMemoryTagging(false); + mController.setChecked(false); + assertThat(mController.getSummary()) + .isEqualTo(mContext.getResources().getString(R.string.memtag_off)); + } + + @Test + public void setChecked_isCheckedPending_updatesSummary() { + ZygoteShadow.setSupportsMemoryTagging(false); + mController.setChecked(true); + assertThat(mController.getSummary()) + .isEqualTo(mContext.getResources().getString(R.string.memtag_on_pending)); + } + + @Test + public void setChecked_isUncheckedPending_updatesSummary() { + ZygoteShadow.setSupportsMemoryTagging(true); + mController.setChecked(false); + assertThat(mController.getSummary()) + .isEqualTo(mContext.getResources().getString(R.string.memtag_off_pending)); + } + + @Test + public void setChecked_isCheckedPending_showsDialog() { + ZygoteShadow.setSupportsMemoryTagging(false); + mController.setChecked(true); + onView(withText(R.string.memtag_reboot_title)).inRoot(isDialog()); + } + + @Test + public void setChecked_isUncheckedPending_showsDialog() { + ZygoteShadow.setSupportsMemoryTagging(true); + mController.setChecked(false); + onView(withText(R.string.memtag_reboot_title)).inRoot(isDialog()); + } + + @Test + public void setChecked_isChecked_doesNotShowDialog() { + ZygoteShadow.setSupportsMemoryTagging(false); + mController.setChecked(false); + onView(withText(R.string.memtag_reboot_title)).inRoot(isDialog()).check(doesNotExist()); + } + + @Test + public void setChecked_isUnchecked_doesNotShowDialog() { + ZygoteShadow.setSupportsMemoryTagging(true); + mController.setChecked(true); + onView(withText(R.string.memtag_reboot_title)).inRoot(isDialog()).check(doesNotExist()); + } + + @Test + public void updateState_disabledByAdmin_disablesPreference() { + ShadowRestrictedLockUtilsInternal.setMteIsDisabled(true); + RestrictedSwitchPreference preference = new RestrictedSwitchPreference(mContext); + mController.updateState(preference); + assertThat(preference.isDisabledByAdmin()).isTrue(); + } + + private static final class TestActivity extends FragmentActivity { + private static final int CONTAINER_VIEW_ID = 1234; + + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + + FragmentContainerView contentView = new FragmentContainerView(this); + contentView.setId(CONTAINER_VIEW_ID); + setContentView(contentView); + } + } +} diff --git a/tests/robotests/src/com/android/settings/development/RebootWithMtePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/RebootWithMtePreferenceControllerTest.java index d6b10319ef8..f1e7d3f2530 100644 --- a/tests/robotests/src/com/android/settings/development/RebootWithMtePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/RebootWithMtePreferenceControllerTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertTrue; import android.content.Context; import android.os.SystemProperties; +import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.test.core.app.ApplicationProvider; @@ -42,14 +43,15 @@ import org.robolectric.shadows.ShadowSystemProperties; public class RebootWithMtePreferenceControllerTest { private Context mContext; private RebootWithMtePreferenceController mController; - @Mock private DevelopmentSettingsDashboardFragment mSettings; + @Mock private Fragment mFragment; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext(); - mController = new RebootWithMtePreferenceController(mContext, mSettings); + mController = new RebootWithMtePreferenceController(mContext); + mController.setFragment(mFragment); } @Test diff --git a/tests/robotests/src/com/android/settings/security/ZygoteShadow.java b/tests/robotests/src/com/android/settings/security/ZygoteShadow.java index 23b30faf045..c7b11c35a29 100644 --- a/tests/robotests/src/com/android/settings/security/ZygoteShadow.java +++ b/tests/robotests/src/com/android/settings/security/ZygoteShadow.java @@ -25,7 +25,7 @@ import org.robolectric.annotation.Implements; public class ZygoteShadow { private static boolean sSupportsMemoryTagging; - static void setSupportsMemoryTagging(boolean value) { + public static void setSupportsMemoryTagging(boolean value) { sSupportsMemoryTagging = value; }