Add persistent MTE toggle to development options.

Bug: 245624194
Change-Id: I83283eb74360ec0eccd6b236b42eb987a580ec2c
This commit is contained in:
Florian Mayer
2023-01-27 13:22:54 -08:00
parent 73d7224561
commit 5b93b73df1
16 changed files with 663 additions and 29 deletions

View File

@@ -6434,6 +6434,7 @@
<string name="help_url_privacy_dashboard" translatable="false"></string>
<string name="help_url_memtag" translatable="false"></string>
<string name="help_url_development_memtag" translatable="false"></string>
<string name="help_url_network_dashboard" translatable="false"></string>
<string name="help_url_connected_devices" translatable="false"></string>
<string name="help_url_apps_and_notifications" translatable="false"></string>
@@ -10828,14 +10829,26 @@
<!-- Title for the button to initiate a heap dump for the system server. [CHAR LIMIT=NONE] -->
<string name="capture_system_heap_dump_title">Capture system heap dump</string>
<!-- [CHAR LIMIT=32] Name of MTE page in "Developer Options" and heading of page. -->
<string name="development_memtag_page_title">Memory Tagging Extension</string>
<!-- [CHAR LIMIT=52] Label for button to turn on / off MTE protection.-->
<string name="development_memtag_intro">Memory Tagging Extension (MTE) makes it easier to find memory safety issues in your app and make native code in it more secure.</string>
<string name="development_memtag_footer">Turning on MTE might cause slower device performance.</string>
<string name="development_memtag_learn_more">Learn more about MTE</string>
<string name="development_memtag_toggle">Enable MTE until you turn it off</string>
<!-- [CHAR LIMIT=NONE] Message shown in dialog prompting user to reboot device to turn on MTE.-->
<string name="development_memtag_reboot_message_on">You\u0027ll need to restart your device to turn on MTE.</string>
<!-- [CHAR LIMIT=NONE] Message shown in dialog prompting user to reboot device to turn off MTE.-->
<string name="development_memtag_reboot_message_off">You\u0027ll need to restart your device to turn off MTE.</string>
<!-- Title for the button to reboot with MTE enabled. [CHAR LIMIT=NONE] -->
<string name="reboot_with_mte_title">Reboot with MTE</string>
<string name="reboot_with_mte_message">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.</string>
<string name="reboot_with_mte_title">Enable MTE for a single session</string>
<string name="reboot_with_mte_message">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.</string>
<!-- Subtext for button if MTE is not enabled through Advanced memory protection. -->
<string name="reboot_with_mte_summary">Try MTE for a single boot for app development</string>
<string name="reboot_with_mte_summary">Restart for a single session with MTE enabled</string>
<!-- Subtext for button if MTE is already enabled through Advanced memory protection.
The string for "Advanced memory protection" needs to match "memtag_toggle" above -->
<string name="reboot_with_mte_already_enabled">MTE is enabled through Advanced memory protection</string>
<string name="reboot_with_mte_already_enabled">MTE is already enabled</string>
<!-- Toast that is shown when the user initiates capturing a heap dump for the system server. [CHAR LIMIT=NONE] -->
<string name="capturing_system_heap_dump_message">Capturing system heap dump</string>
<!-- Toast that is shown if there's an error capturing the user initiated heap dump. [CHAR LIMIT=NONE] -->

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/development_memtag_page_title">
<com.android.settingslib.widget.TopIntroPreference
android:title="@string/development_memtag_intro"
settings:searchable="false"/>
<com.android.settingslib.RestrictedSwitchPreference
android:key="development_memtag"
android:title="@string/development_memtag_toggle"
settings:controller="com.android.settings.development.DevelopmentMemtagPreferenceController" />
<Preference
android:key="reboot_with_mte"
android:title="@string/reboot_with_mte_title"
settings:controller="com.android.settings.development.RebootWithMtePreferenceController" />
<com.android.settingslib.widget.FooterPreference
android:title="@string/development_memtag_footer"
android:key="memtag_footer"
settings:searchable="false"
settings:controller="com.android.settings.development.DevelopmentMemtagFooterPreferenceController" />
</PreferenceScreen>

View File

@@ -45,8 +45,9 @@
android:title="@string/capture_system_heap_dump_title" />
<Preference
android:key="reboot_with_mte"
android:title="@string/reboot_with_mte_title" />
android:key="development_memtag_page"
android:title="@string/development_memtag_page_title"
android:fragment="com.android.settings.development.DevelopmentMemtagPage" />
<Preference
android:key="local_backup_password"

View File

@@ -0,0 +1,56 @@
/*
* 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.security;
import android.content.Context;
import android.text.TextUtils;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.widget.FooterPreference;
/** Footer for face settings showing the help text and help link. */
public class DevelopmentMemtagFooterPreferenceController extends BasePreferenceController {
public DevelopmentMemtagFooterPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE_UNSEARCHABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
// Set up learn more link.
FooterPreference prefFooter = screen.findPreference(getPreferenceKey());
String helpUrl = mContext.getString(R.string.help_url_development_memtag);
if (prefFooter != null && !TextUtils.isEmpty(helpUrl)) {
prefFooter.setLearnMoreAction(
v ->
mContext.startActivity(
HelpUtils.getHelpIntent(
mContext, helpUrl, /* backupContext= */ "")));
prefFooter.setLearnMoreText(mContext.getString(R.string.development_memtag_learn_more));
}
}
}

View File

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

View File

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

View File

@@ -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());
}
}

View File

@@ -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));

View File

@@ -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()) {

View File

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

View File

@@ -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();

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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<TestActivity> 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);
}
}
}

View File

@@ -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

View File

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