diff --git a/tests/Enable16KbTests/Android.bp b/tests/Enable16KbTests/Android.bp new file mode 100644 index 00000000000..57c6ef6503c --- /dev/null +++ b/tests/Enable16KbTests/Android.bp @@ -0,0 +1,62 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["packages_apps_Settings_license"], + default_team: "trendy_team_android_kernel", +} + +android_test_helper_app { + name: "test_16kb_app", + srcs: ["test_16kb_app/src/**/*.java"], + manifest: "test_16kb_app/test_16kb_app.xml", + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "platform-test-annotations", + "settings-helper", + "sysui-helper", + "truth", + "flag-junit", + ], + platform_apis: true, + certificate: "platform", + test_suites: ["general-tests"], + libs: [ + "android.test.runner", + "android.test.base", + ], +} + +java_test_host { + name: "Enable16KbTest", + // Include all test java files + srcs: ["src/**/*.java"], + static_libs: [ + "junit", + "platform-test-annotations", + "truth", + ], + libs: [ + "tradefed", + "compatibility-host-util", + "compatibility-tradefed", + ], + data: [ + ":test_16kb_app", + ], + test_suites: ["general-tests"], + test_config: "AndroidTest.xml", +} diff --git a/tests/Enable16KbTests/AndroidTest.xml b/tests/Enable16KbTests/AndroidTest.xml new file mode 100644 index 00000000000..3309e32fa78 --- /dev/null +++ b/tests/Enable16KbTests/AndroidTest.xml @@ -0,0 +1,45 @@ + + + + + diff --git a/tests/Enable16KbTests/src/com/android/test/Enable16KbTest.java b/tests/Enable16KbTests/src/com/android/test/Enable16KbTest.java new file mode 100644 index 00000000000..b611d617907 --- /dev/null +++ b/tests/Enable16KbTests/src/com/android/test/Enable16KbTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import android.platform.test.annotations.AppModeFull; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; +import com.android.tradefed.util.RunUtil; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.BufferedReader; +import java.io.StringReader; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class Enable16KbTest extends BaseHostJUnit4Test { + private static final String TEST_APP_NAME = "test_16kb_app.apk"; + + private static final String APP_PACKAGE = "com.android.settings.development.test"; + + private static final String TEST_NAME = "Enable16KbDeviceTest"; + + private static final String SWITCH_TO_EXT4 = "enable16k_switchToExt4"; + + private static final String SWITCH_TO_16KB = "enable16k_switchTo16Kb"; + + private static final String SWITCH_TO_4KB = "enable16k_switchTo4Kb"; + private static final String DISABLE_DEV_OPTION = "enable16k_disableDeveloperOption"; + + @Test + @AppModeFull + public void enable16KbToggle() throws Exception { + assertTrue(isPackageInstalled(APP_PACKAGE)); + + // Check if developer option is enabled otherwise exit + getDevice().enableAdbRoot(); + String result = getDevice().getProperty("ro.product.build.16k_page.enabled"); + assumeTrue("true".equals(result)); + + // This test can be run on OEM unlocked device only as unlocking bootloader requires + // manual intervention. + result = getDevice().getProperty("ro.boot.flash.locked"); + assumeTrue("0".equals(result)); + + getDevice().executeShellCommand("am start -a com.android.setupwizard.FOUR_CORNER_EXIT"); + + // Enables developer option and switch to ext4 + runTestAndWait(SWITCH_TO_EXT4); + + getDevice().enableAdbRoot(); + getDevice().executeShellCommand("am start -a com.android.setupwizard.FOUR_CORNER_EXIT"); + assertTrue(verifyExt4()); + + // Device will wiped. need to install test package again. + installTestApp(); + + // Enable developer option and switch to 16kb kernel and Check page size + runTestAndWait(SWITCH_TO_16KB); + result = getDevice().executeShellCommand("getconf PAGE_SIZE"); + assertEquals("16384", result.strip()); + + // switch back to 4kb kernel and check page size + runTestAndWait(SWITCH_TO_4KB); + result = getDevice().executeShellCommand("getconf PAGE_SIZE"); + assertEquals("4096", result.strip()); + + // Verify that developer options can't be turned off + runDeviceTests(APP_PACKAGE, APP_PACKAGE + "." + TEST_NAME, DISABLE_DEV_OPTION); + } + + private void installTestApp() throws Exception { + DeviceTestRunOptions options = new DeviceTestRunOptions(null /* unused */); + options.setApkFileName(TEST_APP_NAME); + options.setInstallArgs("-r"); + installPackage(options); + assertTrue(isPackageInstalled(APP_PACKAGE)); + } + + private void runTestAndWait(String testMethodName) throws Exception { + runDeviceTests(APP_PACKAGE, APP_PACKAGE + "." + TEST_NAME, testMethodName); + // Device is either formatting or applying update. It usually takes 3 minutes to boot. + RunUtil.getDefault().sleep(180000); + // Wait for 2 mins device to be online againg + getDevice().waitForDeviceOnline(120000); + } + + private boolean verifyExt4() throws Exception { + String result = getDevice().executeShellCommand("cat /proc/mounts"); + BufferedReader br = new BufferedReader(new StringReader(result)); + String line; + while ((line = br.readLine()) != null) { + final String[] fields = line.split(" "); + final String partition = fields[1]; + final String fsType = fields[2]; + if (partition.equals("/data") && fsType.equals("ext4")) { + return true; + } + } + return false; + } +} diff --git a/tests/Enable16KbTests/test_16kb_app/src/com/android/settings/development/test/Enable16KbDeviceTest.java b/tests/Enable16KbTests/test_16kb_app/src/com/android/settings/development/test/Enable16KbDeviceTest.java new file mode 100644 index 00000000000..e5ccbb92da0 --- /dev/null +++ b/tests/Enable16KbTests/test_16kb_app/src/com/android/settings/development/test/Enable16KbDeviceTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development.test; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.os.RemoteException; +import android.provider.Settings; +import android.system.helpers.SettingsHelper; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.Direction; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class Enable16KbDeviceTest { + private static final long TIMEOUT = 2000; + + private static final String ENABLE_16K_TOGGLE = "Boot with 16KB page size"; + private static final String BUILD_NUMBER = "Build number"; + private static final String USE_DEVELOPER_OPTIONS = "Use developer options"; + private static final String EXT4_CONFIRMATION = "Erase all data"; + private static final String EXT4_TITLE = "Reformat device to ext4? (required for 16KB mode)"; + private static final String TOGGLE_16K_TITLE = "Switch from 4KB mode to 16KB mode"; + private static final String TOGGLE_4K_TITLE = "Switch from 16KB mode to 4KB mode"; + private static final String ANDROID_WIDGET_SCROLLVIEW = "android.widget.ScrollView"; + private static final String OKAY = "OK"; + private static final String NOTIFICATION_TITLE_4K = "Using 4KB page-agnostic mode"; + private static final String NOTIFICATION_TITLE_16K = "Using 16KB page-agnostic mode"; + + private Context mContext; + private UiDevice mDevice; + private SettingsHelper mHelper; + + @Before + public void setUp() throws Exception { + mContext = getInstrumentation().getTargetContext(); + mDevice = UiDevice.getInstance(getInstrumentation()); + mHelper = SettingsHelper.getInstance(); + try { + mDevice.setOrientationNatural(); + } catch (RemoteException e) { + throw new RuntimeException("failed to freeze device orientation", e); + } + + mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP"); + mDevice.executeShellCommand("wm dismiss-keyguard"); + } + + private void unlockDeveloperOptions() throws Exception { + SettingsHelper.launchSettingsPage(mContext, Settings.ACTION_DEVICE_INFO_SETTINGS); + // Click 7 times on build number to unlock the dev options + for (int i = 0; i < 7; i++) { + mHelper.clickSetting(BUILD_NUMBER); + } + } + + @Test + public void enable16k_switchToExt4() throws Exception { + unlockDeveloperOptions(); + SettingsHelper.launchSettingsPage( + mContext, Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + clickOnObject(By.text(ENABLE_16K_TOGGLE)); + + // Verify that ext4 toggle is visible + verifyTextOnScreen(EXT4_TITLE); + + mDevice.wait(Until.findObject(By.text(EXT4_CONFIRMATION)), TIMEOUT).click(); + } + + @Test + public void enable16k_switchTo16Kb() throws Exception { + // Device will be in 4kb mode + openPersistentNotification(NOTIFICATION_TITLE_4K); + unlockDeveloperOptions(); + SettingsHelper.launchSettingsPage( + mContext, Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + + clickOnObject(By.text(ENABLE_16K_TOGGLE)); + // Verify that text is displayed to switch to 16kb + verifyTextOnScreen(TOGGLE_16K_TITLE); + + mDevice.wait(Until.findObject(By.text(OKAY)), TIMEOUT).click(); + } + + @Test + public void enable16k_switchTo4Kb() throws Exception { + // Device will be in 16kb mode + openPersistentNotification(NOTIFICATION_TITLE_16K); + SettingsHelper.launchSettingsPage( + mContext, Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + + clickOnObject(By.text(ENABLE_16K_TOGGLE)); + // Verify that text is displayed to switch to 4kb + verifyTextOnScreen(TOGGLE_4K_TITLE); + + mDevice.wait(Until.findObject(By.text(OKAY)), TIMEOUT).click(); + } + + private void clickOnObject(BySelector target) { + mDevice.waitForWindowUpdate(null, TIMEOUT); + UiObject2 scrollView = + mDevice.wait( + Until.findObject(By.scrollable(true).clazz(ANDROID_WIDGET_SCROLLVIEW)), + TIMEOUT); + UiObject2 targetObject = scrollTo(scrollView, target, Direction.DOWN); + assertTrue(targetObject != null); + targetObject.click(); + } + + private UiObject2 scrollTo(UiObject2 scrollable, BySelector target, Direction direction) { + while (!mDevice.hasObject(target) && scrollable.scroll(direction, 1.0f)) { + // continue + } + if (!mDevice.hasObject(target)) { + scrollable.scroll(direction, 1.0f); + } + return mDevice.findObject(target); + } + + private void verifyTextOnScreen(String displayedText) { + UiObject2 targetObject = mDevice.wait(Until.findObject(By.text(displayedText)), TIMEOUT); + assertTrue(targetObject != null); + } + + private void openPersistentNotification(String title) { + mDevice.openNotification(); + verifyTextOnScreen(title); + mDevice.wait(Until.findObject(By.text(title)), TIMEOUT).click(); + mDevice.waitForWindowUpdate(null, TIMEOUT); + verifyTextOnScreen(title); + mDevice.wait(Until.findObject(By.text(OKAY)), TIMEOUT).click(); + mDevice.waitForWindowUpdate(null, TIMEOUT); + } + + @Test + public void enable16k_disableDeveloperOption() throws Exception { + // Device will be in 4KB mode when this test will be run + SettingsHelper.launchSettingsPage( + mContext, Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); + mDevice.wait(Until.findObject(By.text(USE_DEVELOPER_OPTIONS)), TIMEOUT).click(); + verifyTextOnScreen(NOTIFICATION_TITLE_4K); + mDevice.wait(Until.findObject(By.text(OKAY)), TIMEOUT).click(); + } +} diff --git a/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml b/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml new file mode 100644 index 00000000000..8fe9ad52fed --- /dev/null +++ b/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file