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;
}