fix: 引入Settings的Module

This commit is contained in:
2024-12-10 14:57:24 +08:00
parent ad8fc8731d
commit df105485bd
6934 changed files with 896168 additions and 2 deletions

View File

@@ -0,0 +1,38 @@
//############################################################
// Settings Component test target. #
//############################################################
package {
default_team: "trendy_team_android_settings_app",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "packages_apps_Settings_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["packages_apps_Settings_license"],
}
android_test {
name: "SettingsComponentTests",
certificate: "platform",
privileged: true,
srcs: [
"src/**/*.java",
],
static_libs: [
"truth",
"androidx.test.core",
"androidx.test.espresso.core",
"androidx.test.espresso.intents-nodeps",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"mockito-target",
],
test_suites: ["device-tests"],
optimize: {
enabled: false,
},
instrumentation_for: "Settings",
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.settings.tests.component"
android:sharedUserId="android.uid.systemui">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application/>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.settings"
android:label="Settings Test Cases">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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.
-->
<configuration description="Runs Settings Test Cases.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="SettingsComponentTests.apk" />
</target_preparer>
<option name="test-tag" value="SettingsComponentTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.settings.tests.component" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>

View File

@@ -0,0 +1,3 @@
# People who can approve changes for submission
jyhsu@google.com
syaoranx@google.com

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C) 2021 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.biometrics;
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.espresso.intent.Intents;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.settings.biometrics.face.FaceEnrollIntroduction;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmLockPassword;
import com.android.settings.testutils.AdbUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class BiometricEnrollActivityTest {
private static final String TAG = "BiometricEnrollActivityTest";
private static final int ADB_TIMEOUT_MS = 5000;
private static final String TEST_PIN = "1234";
private final Context mContext = ApplicationProvider.getApplicationContext();
private boolean mHasFace;
private boolean mHasFingerprint;
private boolean mIsFaceStrong;
private boolean mIsFingerprintStrong;
@Before
public void setup() {
Intents.init();
final PackageManager pm = mContext.getPackageManager();
mHasFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
mHasFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
if (mHasFace) {
final FaceManager faceManager = mContext.getSystemService(FaceManager.class);
final List<FaceSensorPropertiesInternal> faceProperties =
faceManager.getSensorPropertiesInternal();
if (!faceProperties.isEmpty()) {
final FaceSensorPropertiesInternal faceProp = faceProperties.get(0);
mIsFaceStrong = faceProp.sensorStrength == SensorProperties.STRENGTH_STRONG;
}
}
if (mHasFingerprint) {
final FingerprintManager fingerprintManager = mContext.getSystemService(
FingerprintManager.class);
final List<FingerprintSensorPropertiesInternal> fingerProperties =
fingerprintManager.getSensorPropertiesInternal();
if (!fingerProperties.isEmpty()) {
final FingerprintSensorPropertiesInternal fingerProp = fingerProperties.get(0);
mIsFingerprintStrong =
fingerProp.sensorStrength == SensorProperties.STRENGTH_STRONG;
}
}
}
@After
public void teardown() throws Exception {
Intents.release();
AdbUtils.checkStringInAdbCommandOutput(TAG, "locksettings clear --old " + TEST_PIN,
"", "", ADB_TIMEOUT_MS);
}
@Test
public void launchWithoutPin_setsPin() {
try (ActivityScenario<BiometricEnrollActivity> scenario =
ActivityScenario.launch(getIntent())) {
intended(hasComponent(ChooseLockGeneric.class.getName()));
if (mHasFace && mHasFingerprint) {
intended(hasExtra(EXTRA_KEY_FOR_BIOMETRICS, true));
} else if (mHasFace) {
intended(hasExtra(EXTRA_KEY_FOR_FACE, true));
} else if (mHasFingerprint) {
intended(hasExtra(EXTRA_KEY_FOR_FINGERPRINT, true));
}
}
}
@Test
public void launchWithPin_confirmsPin() throws Exception {
setPin();
try (ActivityScenario<BiometricEnrollActivity> scenario =
ActivityScenario.launch(getIntent())) {
intended(hasComponent(ConfirmLockPassword.InternalActivity.class.getName()));
}
}
@Test
public void launchWithPinAndPwHandle_confirmsPin() throws Exception {
assumeTrue(mHasFace || mHasFingerprint);
setPin();
final Intent intent = getIntent(true /* useInternal */);
LockPatternChecker.verifyCredential(new LockPatternUtils(mContext),
LockscreenCredential.createPin(TEST_PIN), UserHandle.myUserId(),
LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, (response, timeoutMs) -> {
assertThat(response.containsGatekeeperPasswordHandle()).isTrue();
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
response.getGatekeeperPasswordHandle());
}).get();
try (ActivityScenario<BiometricEnrollActivity> scenario =
ActivityScenario.launch(intent)) {
intended(hasComponent(mHasFace && !mHasFingerprint
? FaceEnrollIntroduction.class.getName()
: FingerprintEnrollIntroduction.class.getName()));
}
}
@Test
public void launchWithStrongBiometricAllowed_doNotEnrollWeak() throws Exception {
assumeTrue(mHasFace || mHasFingerprint);
// Allow only strong biometrics
Intent intent = getIntent();
intent.putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG);
try (ActivityScenario<BiometricEnrollActivity> scenario =
ActivityScenario.launch(intent)) {
intended(hasComponent(ChooseLockGeneric.class.getName()));
if (mIsFaceStrong && mIsFingerprintStrong) {
intended(hasExtra(EXTRA_KEY_FOR_BIOMETRICS, true));
} else if (mIsFaceStrong) {
intended(hasExtra(EXTRA_KEY_FOR_FACE, true));
} else if (mIsFingerprintStrong) {
intended(hasExtra(EXTRA_KEY_FOR_FINGERPRINT, true));
}
}
}
private Intent getIntent() {
return getIntent(false /* useInternal */);
}
private Intent getIntent(boolean useInternal) {
final Intent intent = new Intent(mContext, useInternal
? BiometricEnrollActivity.InternalActivity.class : BiometricEnrollActivity.class);
intent.setAction(ACTION_BIOMETRIC_ENROLL);
return intent;
}
private static void setPin() throws Exception {
assertThat(AdbUtils.checkStringInAdbCommandOutput(TAG, "locksettings set-pin " + TEST_PIN,
"Pin set to ", "'" + TEST_PIN + "'", ADB_TIMEOUT_MS)).isTrue();
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2021 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.display.darkmode;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.app.TimePickerDialog;
import android.app.UiModeManager;
import android.content.Intent;
import android.content.res.Configuration;
import android.provider.Settings;
import android.util.Log;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.lifecycle.Stage;
import com.android.settings.testutils.CommonUtils;
import com.android.settings.testutils.UiUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.LocalTime;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DarkThemeScheduleComponentTest {
private static final int DIALOG_START_TIME = 0;
private static final int DIALOG_END_TIME = 1;
/** The identifier for the positive button. */
private static final int BUTTON_POSITIVE = -1;
public final String TAG = this.getClass().getName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@Rule
public ActivityScenarioRule<com.android.settings.Settings.DarkThemeSettingsActivity> rule =
new ActivityScenarioRule<>(
new Intent(
Settings.ACTION_DARK_THEME_SETTINGS).setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK));
private UiModeManager mUiModeManager;
@Before
public void setUp() {
mUiModeManager = mInstrumentation.getTargetContext().getSystemService(UiModeManager.class);
if (mUiModeManager.getNightMode() != UiModeManager.MODE_NIGHT_NO) {
mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
}
}
private void test_step_for_custom_time(int startTimeDiff, int endTimeDiff) {
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_CUSTOM);
Fragment f =
((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(0);
DarkModeSettingsFragment fragment = (DarkModeSettingsFragment) f;
setCustomTime(fragment, DIALOG_START_TIME, LocalTime.now().plusMinutes(startTimeDiff));
setCustomTime(fragment, DIALOG_END_TIME, LocalTime.now().plusMinutes(endTimeDiff));
// The night mode need to reopen the screen to trigger UI change after mode change.
CommonUtils.reopenScreen();
});
// Relaunch the scenario to make sure UI apply new mode.
scenario.onActivity(activity -> {
Log.d(TAG, "Activity Recreated!");
UiUtils.waitForActivitiesInStage(2000, Stage.RESUMED);
});
}
@Test
public void test_dark_mode_in_custom_time() {
test_step_for_custom_time(-1, 11);
assertThat(checkNightMode(true)).isTrue();
}
@Test
public void test_dark_mode_after_custom_time() {
test_step_for_custom_time(-11, -1);
assertThat(checkNightMode(false)).isTrue();
}
@Test
public void test_dark_mode_before_custom_time() {
test_step_for_custom_time(2, 20);
assertThat(checkNightMode(false)).isTrue();
}
/**
* Sets custom time for night mode.
*
* @param fragment The DarkModeSettingsFragment.
* @param dialogId Dialog id for start time or end time.
* @param time The time to be set.
*/
private void setCustomTime(DarkModeSettingsFragment fragment, int dialogId, LocalTime time) {
Log.d(TAG, "Start to set custom time " + (dialogId == DIALOG_START_TIME ? "StartTime"
: "EndTime") + " to " + time.getHour() + ":" + time.getMinute());
TimePickerDialog startTimeDialog = (TimePickerDialog) fragment.onCreateDialog(dialogId);
startTimeDialog.updateTime(time.getHour(), time.getMinute());
startTimeDialog.onClick(startTimeDialog, BUTTON_POSITIVE);
}
private boolean checkNightMode(boolean isNightMode) {
int mask = (isNightMode ? Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO);
int mode = mInstrumentation.getTargetContext().getResources().getConfiguration().uiMode;
return (mode & mask) != 0;
}
@After
public void tearDown() {
Log.d(TAG, "tearDown.");
if (mUiModeManager.getNightMode() != UiModeManager.MODE_NIGHT_NO) {
mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2020 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.fuelgauge.batterysaver;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.Settings.BatterySaverSettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.testutils.AdbUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class BatterySaverButtonPreferenceControllerComponentTest {
private static final String TAG =
BatterySaverButtonPreferenceControllerComponentTest.class.getSimpleName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final PowerManager mManager =
(PowerManager) mInstrumentation.getTargetContext().getSystemService(
Context.POWER_SERVICE);
@Rule
public ActivityScenarioRule<BatterySaverSettingsActivity> rule = new ActivityScenarioRule<>(
new Intent(
Settings.ACTION_BATTERY_SAVER_SETTINGS).setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK));
@Before
public void setUp() throws Exception {
mInstrumentation.getUiAutomation().executeShellCommand("dumpsys battery unplug");
mInstrumentation.getUiAutomation().executeShellCommand("settings get global low_power 0");
}
private BatterySaverButtonPreferenceController get_battery_saver_controller(Activity activity) {
BatterySaverButtonPreferenceController controller =
new BatterySaverButtonPreferenceController(
ApplicationProvider.getApplicationContext(), "battery_saver");
Fragment f =
((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(0);
controller.displayPreference(((SettingsPreferenceFragment) f).getPreferenceScreen());
return controller;
}
@Test
public void test_check_battery_saver_button() throws Exception {
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
BatterySaverButtonPreferenceController controller =
get_battery_saver_controller(activity);
controller.setChecked(true);
checkPowerSaverMode(true);
controller.setChecked(false);
checkPowerSaverMode(false);
});
}
@After
public void tearDown() {
mInstrumentation.getUiAutomation().executeShellCommand("settings get global low_power 0");
mInstrumentation.getUiAutomation().executeShellCommand("dumpsys battery reset");
}
private void checkPowerSaverMode(boolean enabled) {
//Check through adb. Note that this needs to be done first, or a wait and poll needs to be
//done to the manager.isPowerSaveMode(), because calling isPowerSaveMode immediately after
//setting it does not return true. It takes a while for isPowerSaveMode() to return the
//up-to-date value.
try {
assertThat(
AdbUtils.checkStringInAdbCommandOutput(TAG, "settings get global low_power",
null, enabled ? "1" : "0", 1000)).isTrue();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
assert_().fail();
}
//Check through manager
assertThat(mManager.isPowerSaveMode() == enabled).isTrue();
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2021 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.homepage;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Intent;
import android.provider.Settings;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class HomepageComponentTest {
public final String TAG = this.getClass().getSimpleName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@Test
public void test_launch_all_settings_in_home()
throws ClassNotFoundException {
List<Intent> launchIntents = ImmutableList.of(
// Wifi
// Implemented in WifiSettings2ActivityTest
// Connected devices
new Intent(Settings.ACTION_BLUETOOTH_SETTINGS),
// Applications
new Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS),
// Notifications
new Intent(Settings.ACTION_NOTIFICATION_SETTINGS),
// Display
new Intent(Settings.ACTION_DISPLAY_SETTINGS),
// Battery
// Implemented in fuelgauge.batterysaver
// Storage
new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS),
// Sound
new Intent(Settings.ACTION_SOUND_SETTINGS),
// Display
new Intent(Settings.ACTION_DISPLAY_SETTINGS),
// Wallpaper
new Intent(mInstrumentation.getTargetContext(), Class.forName(
"com.android.settings.wallpaper.WallpaperSuggestionActivity")),
// A11y
new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
// Security
new Intent(Settings.ACTION_SECURITY_SETTINGS),
// Privacy
new Intent(Settings.ACTION_PRIVACY_SETTINGS),
// Location
new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),
// Emergency ? EmergencyDashboardFragment
// TODO: find out launch method
// Password & Account
new Intent(Settings.ACTION_SYNC_SETTINGS),
// Digital wellbeing
// Use IA link
new Intent().setComponent(
new ComponentName(
"com.google.android.apps.wellbeing",
"com.google.android.apps.wellbeing.settings"
+ ".TopLevelSettingsActivity")),
// Google
// Use IA link
new Intent().setComponent(
new ComponentName(
"com.google.android.gms",
"com.google.android.gms.app.settings.GoogleSettingsIALink")),
// System ?
// TODO: find out launch method.
// About
new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS)
);
for (Intent intent : launchIntents) {
Log.d(TAG, "Start to launch intent " + intent.getAction());
try {
mInstrumentation.getTargetContext()
.startActivity(intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} catch (Exception e) {
Log.e(TAG, "Launch with exception. " + e.toString());
assert_().fail();
}
// Launch success without exception.
assertThat(Boolean.TRUE).isTrue();
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2021 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.network;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.testutils.UiUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class AirplaneModePreferenceControllerComponentTest {
// Airplane on off status
private static final int ON = 1;
private static final int OFF = 0;
public final String TAG = this.getClass().getName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private boolean mOriginAirplaneModeIsOn;
@Before
public void setUp() {
// Make sure origin airplane mode is OFF.
mOriginAirplaneModeIsOn = is_airplane_mode_on();
if (mOriginAirplaneModeIsOn) {
Log.d(TAG, "Origin airplane mode is on, turn it off.");
Settings.Global.putInt(mInstrumentation.getTargetContext().getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, OFF);
}
}
/**
* Tests on/off airplane mode repeatedly.
* Previously, a bug describe that crash issue if user on off airplane mode repeatedly.
* This case try to switch on & off airplane mode for 10 times to check crash issue.
*/
@Test
public void test_on_off_airplane_mode_multiple_times() {
AirplaneModePreferenceController controller =
new AirplaneModePreferenceController(mInstrumentation.getTargetContext(),
SettingsSlicesContract.KEY_AIRPLANE_MODE);
for (int i = 0; i < 10; ++i) {
Log.d(TAG, "Test #" + (i + 1));
controller.setChecked(true);
assertThat(UiUtils.waitUntilCondition(1000,
() -> is_airplane_mode_on())).isTrue();
controller.setChecked(false);
assertThat(UiUtils.waitUntilCondition(1000,
() -> !is_airplane_mode_on())).isTrue();
}
}
private boolean is_airplane_mode_on() {
return Settings.System.getInt(
mInstrumentation.getTargetContext().getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, OFF) != 0;
}
@After
public void tearDown() {
if (is_airplane_mode_on() != mOriginAirplaneModeIsOn) {
Settings.Global.putInt(mInstrumentation.getTargetContext().getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, (
mOriginAirplaneModeIsOn ? ON : OFF));
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (C) 2021 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.network.telephony;
import static com.android.settings.testutils.CommonUtils.set_wifi_enabled;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleOwner;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.Settings;
import com.android.settings.testutils.CommonUtils;
import com.android.settings.testutils.UiUtils;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity;
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.io.IOException;
import java.net.URL;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MobileDataPreferenceControllerComponentTest {
@Mock
private Lifecycle mLifecycle;
@Mock
private LifecycleOwner mLifecycleOwner;
public static final int TIMEOUT = 2000;
private static int sSubscriptionId = 2;
public final String TAG = this.getClass().getName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final WifiManager mWifiManager =
(WifiManager) mInstrumentation.getTargetContext().getSystemService(
Context.WIFI_SERVICE);
private final TelephonyManager mTelephonyManager =
(TelephonyManager) mInstrumentation.getTargetContext().getSystemService(
Context.TELEPHONY_SERVICE);
private final TelecomManager mTelecomManager =
(TelecomManager) mInstrumentation.getTargetContext().getSystemService(
Context.TELECOM_SERVICE);
@Rule
public ActivityScenarioRule<Settings.MobileNetworkActivity>
rule = new ActivityScenarioRule<>(
new Intent(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
private boolean mOriginDataEnabled;
private boolean mOriginWifiEnabled;
@Before
public void setUp() {
mOriginWifiEnabled = mWifiManager.isWifiEnabled();
// Disable wifi
set_wifi_enabled(false);
// Enable mobile data
mOriginDataEnabled = mTelephonyManager.isDataEnabled();
if (!mOriginDataEnabled) {
mTelephonyManager.enableDataConnectivity();
}
// Current sim card is not available for data network.
sSubscriptionId = SubscriptionManager.getDefaultDataSubscriptionId();
Assume.assumeTrue("Device cannot mobile network! Should ignore test.",
sSubscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
int simState = mTelephonyManager.getSimState();
Assume.assumeTrue("Sim card is not ready. Expect: " + TelephonyManager.SIM_STATE_READY
+ ", Actual: " + simState, simState == TelephonyManager.SIM_STATE_READY);
}
/**
* Tests the mobile network is disabled.
* Precondition:
* Disabled wifi, and enabled mobile network.
* Steps:
* 1. Launch mobile data page.
* 2. Turn off mobile data from switch.
* [Check]
* - Mobile data is turned off via TelephonyManager.
* - Open socket connection https://www.google.net and check the connection failed.
*/
@Test
public void test_disable_mobile_network() {
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
try {
URL url = new URL("https://www.google.net");
MobileDataPreferenceController controller = new MobileDataPreferenceController(
mInstrumentation.getTargetContext(), "mobile_data", mLifecycle,
mLifecycleOwner, sSubscriptionId);
FragmentManager manager = ((FragmentActivity) activity).getSupportFragmentManager();
controller.init(manager, sSubscriptionId, mock(SubscriptionInfoEntity.class), mock(
MobileNetworkInfoEntity.class));
// Make sure mobile network can connect at first.
assertThat(UiUtils.waitUntilCondition(1000,
() -> CommonUtils.connectToURL(url))).isTrue();
Log.d(TAG, "Start to click ");
controller.setChecked(false);
Log.d(TAG, "Set Checked, wait for fully close.");
// Assert the configuration is set.
assertThat(UiUtils.waitUntilCondition(10000,
() -> !mTelephonyManager.isDataEnabled())).isTrue();
// Assert the network is not connectable.
assertThat(UiUtils.waitUntilCondition(1000,
() -> CommonUtils.connectToURL(url))).isFalse();
} catch (IOException e) {
}
});
}
@After
public void tearDown() {
// Restore wifi status wifi
set_wifi_enabled(mOriginWifiEnabled);
// Restore mobile data status
if (mOriginDataEnabled != mTelephonyManager.isDataEnabled()) {
if (mOriginDataEnabled) {
mTelephonyManager.enableDataConnectivity();
} else {
mTelephonyManager.disableDataConnectivity();
}
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 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.notification;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.content.Intent;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.lifecycle.Stage;
import com.android.settings.testutils.CommonUtils;
import com.android.settings.testutils.UiUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class AppNotificationComponentTest {
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final String mNoSlientAppName = "com.google.android.dialer";
public final String TAG = this.getClass().getName();
@Rule
public ActivityScenarioRule<com.android.settings.Settings.AppNotificationSettingsActivity>
rule = new ActivityScenarioRule<>(
new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, mNoSlientAppName)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
/**
* Tests user should not able to modify notification settings for some system apps.
* In this case, test `phone` app that will disabled notification configuration.
* Steps:
* 1. Open notification page of phone app.
* 2. Checks system privilege notification should not able to be changed.
*/
@Test
public void test_special_app_could_not_disable_notification() {
List<String> disabledList = Arrays.asList("Default", "Incoming calls",
"Background Processing", "Missed calls",
"Ongoing calls", "Voicemails");
ActivityScenario ac = rule.getScenario();
ac.onActivity(
activity -> {
View recyclerView = activity.findViewById(
CommonUtils.getResId("recycler_view"));
if (recyclerView == null) {
Log.d("UI_UTILS",
"Target not found: R.id.recycler_view #" + Integer.toHexString(
CommonUtils.getResId("recycler_view")));
UiUtils.dumpView(UiUtils.getFirstViewFromActivity(activity));
assertThat(Boolean.TRUE).isFalse();
}
UiUtils.waitUntilCondition(5000,
() -> recyclerView.findViewById(CommonUtils.getResId("recycler_view"))
!= null);
View mainSwitchBar = recyclerView.findViewById(
CommonUtils.getResId("main_switch_bar"));
assertThat(mainSwitchBar.isEnabled()).isEqualTo(false);
Log.d(TAG, "main switch bar = " + mainSwitchBar.isEnabled());
UiUtils.waitForActivitiesInStage(10000, Stage.RESUMED);
Log.d(TAG, "In stage!.");
UiUtils.dumpView(UiUtils.getFirstViewFromActivity(activity));
// The privileges are under the recycle view. Fetch all of them and check.
ViewGroup viewGroup = (ViewGroup) recyclerView;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
if (viewGroup.getChildAt(i) instanceof LinearLayout) {
// A notification in Settings should have both switch_widget and text.
// There has another circle pin is no belongs to Settings package.
// But belongs to Switch in Android.
View sWidget = viewGroup.getChildAt(i).findViewById(
CommonUtils.getResId("switchWidget"));
TextView sText = viewGroup.getChildAt(i).findViewById(
android.R.id.title);
if (sText != null && sWidget != null
&& disabledList.stream().anyMatch(
str -> str.equals(sText.getText().toString().trim()))) {
assertThat(sWidget.isEnabled()).isFalse();
}
}
}
}
);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2020 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.privacy;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.testutils.AdbUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class EnabledContentCapturePreferenceControllerComponentTest {
private Instrumentation mInstrumentation;
private static final String TAG =
EnabledContentCapturePreferenceControllerComponentTest.class.getSimpleName();
@Before
public void setUp() {
if (null == mInstrumentation) {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
}
}
@Test
public void test_uncheck_content_capture() throws Exception {
content_capture_checkbox_test_helper(false);
}
@Test
public void test_check_content_capture() throws Exception {
content_capture_checkbox_test_helper(true);
}
private void content_capture_checkbox_test_helper(boolean check) throws Exception {
EnableContentCapturePreferenceController enableContentCapturePreferenceController =
new EnableContentCapturePreferenceController(
ApplicationProvider.getApplicationContext(),
"Test_key");
enableContentCapturePreferenceController.setChecked(check);
//Check through adb command
assertThat(AdbUtils.checkStringInAdbCommandOutput(TAG, "dumpsys content_capture",
"Users disabled by Settings: ", check ? "{}" : "{0=true}", 1000)).isTrue();
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2020 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.testutils;
import android.os.ParcelFileDescriptor;
import androidx.test.platform.app.InstrumentationRegistry;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
public class AdbUtils {
public static String getCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i = 1; i < stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(new Object() {
}.getClass().getEnclosingClass().getName()) && ste.getClassName().indexOf(
"java.lang.Thread") != 0) {
return ste.getClassName();
}
}
return null;
}
public static boolean checkStringInAdbCommandOutput(String logTag, String command,
String prefix, String target, int timeoutInMillis) throws Exception {
long start = System.nanoTime();
//Sometimes the change do no reflect in adn output immediately, so need a wait and poll here
while (System.nanoTime() - start < (timeoutInMillis * 1000000)) {
String result = shell(command);
if (result.contains(prefix == null ? "" : prefix)
&& result.contains(target == null ? "" : target)) {
return true;
} else {
Thread.sleep(100);
}
}
return false;
}
public static String shell(String shellCommand) {
String returnValue = "";
try (ParcelFileDescriptor.AutoCloseInputStream in =
new ParcelFileDescriptor.AutoCloseInputStream(
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.executeShellCommand(shellCommand))) {
try (BufferedReader br =
new BufferedReader(
new InputStreamReader(in, StandardCharsets.UTF_8))) {
returnValue = br.lines().collect(Collectors.joining());
}
} catch (IOException e) {
e.printStackTrace();
}
return returnValue;
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2021 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.testutils;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.wifi.WifiManager;
import android.os.Environment;
import android.os.PowerManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import androidx.test.platform.app.InstrumentationRegistry;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
public class CommonUtils {
private static final String TAG = CommonUtils.class.getSimpleName();
private static final Instrumentation sInstrumentation =
InstrumentationRegistry.getInstrumentation();
private static final WifiManager sWifiManager =
(WifiManager) sInstrumentation.getTargetContext().getSystemService(
Context.WIFI_SERVICE);
private static final PowerManager sPowerManager =
(PowerManager) sInstrumentation.getTargetContext().getSystemService(
Context.POWER_SERVICE);
public static void takeScreenshot(Activity activity) {
long now = System.currentTimeMillis();
try {
// image naming and path to include sd card appending name you choose for file
String mPath =
Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";
Log.d(TAG, "screenshot path is " + mPath);
// create bitmap screen capture
View v1 = activity.getWindow().getDecorView().getRootView();
v1.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
File imageFile = new File(mPath);
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 100;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
} catch (Throwable e) {
// Several error may come out with file handling or DOM
e.printStackTrace();
}
}
public static boolean connectToURL(URL url) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
HttpURLConnection connection = null;
try {
connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.connect();
if (HttpURLConnection.HTTP_OK == connection.getResponseCode()) {
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while (null != (line = reader.readLine())) {
response.append(line);
}
Log.d(TAG, "Connection success! " + response.toString());
return true;
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
} finally {
if (null != connection) {
connection.disconnect();
}
}
Log.d(TAG, "End, return false.");
return false;
}
/**
* Return a resource identifier for the given resource name in Settings app.
*
* @param name The name of the desired resource.
* @return int The associated resource identifier. Returns 0 if no such resource was found. (0
* is not a valid resource ID.)
*/
public static int getResId(String name) {
return InstrumentationRegistry.getInstrumentation().getTargetContext().getResources()
.getIdentifier(name, "id", Constants.SETTINGS_PACKAGE_NAME);
}
public static void reopenScreen() {
sPowerManager.goToSleep(SystemClock.uptimeMillis());
// According to local test, we need to sleep to wait it fully processed.
// 1000 ms is a good value to sleep, otherwise it might cause keyDispatchingTimedOut.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
UiUtils.waitUntilCondition(1000, () -> !sPowerManager.isInteractive());
sPowerManager.wakeUp(SystemClock.uptimeMillis());
UiUtils.waitUntilCondition(1000, () -> sPowerManager.isInteractive());
// After power on screen, need to unlock and goto home page.
sPowerManager.wakeUp(1000, PowerManager.WAKE_REASON_POWER_BUTTON, "Wakeup");
}
/**
* Sets wifi status to given enable / disable via ADB command.
*/
public static void set_wifi_enabled(boolean enable) {
final int timeoutMsec = 10000;
Log.d(TAG, "Set wifi status to " + enable);
if (sWifiManager.isWifiEnabled() != enable) {
AdbUtils.shell("svc wifi " + (enable ? "enable" : "disable"));
if (!UiUtils.waitUntilCondition(timeoutMsec,
() -> sWifiManager.isWifiEnabled() == enable)) {
Log.e(TAG, "Cannot set wifi to " + (enable ? "enabl" : "disable") + ", timeout "
+ timeoutMsec + " (ms).");
Log.e(TAG, "See logcat for more information.");
}
Log.d(TAG, "After configuration wifi status = " + sWifiManager.isWifiEnabled());
} else {
Log.d(TAG, "Wifi is enable is already " + enable + ", no need to change.");
}
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2021 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.testutils;
public class Constants {
public static final long ACTIVITY_LAUNCH_WAIT_TIMEOUT = 5000;
public static final long VIEW_APPEAR_WAIT_MEDIUM_TIMEOUT = 5000;
public static final long WIFI_CONNECT_WAIT_TIMEOUT = 15000;
public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 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.testutils;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.FragmentActivity;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;
public class UiUtils {
private static final String TAG = "UI_UTILS";
public static boolean waitUntilCondition(long timeoutInMillis, Supplier<Boolean> condition) {
long start = System.nanoTime();
while (System.nanoTime() - start < (timeoutInMillis * 1000000)) {
try {
//Eat NPE from condition because there's a concurrency issue that when calling
//findViewById when the view hierarchy is still rendering, it sometimes encounter
//null views that may exist few milliseconds before, and causes a NPE.
if (condition.get()) {
return true;
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}
Log.w(TAG, "Condition not match and timeout for waiting " + timeoutInMillis + "(ms).");
return false;
}
public static boolean waitForActivitiesInStage(long timeoutInMillis, Stage stage) {
final Collection<Activity> activities = new ArrayList<>();
waitUntilCondition(Constants.ACTIVITY_LAUNCH_WAIT_TIMEOUT, () -> {
activities.addAll(
ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
Stage.RESUMED));
return activities.size() > 0;
});
return activities.size() > 0;
}
public static void dumpView(View view) {
dumpViewRecursive(view, 0, 0, 0);
}
public static View getFirstViewFromActivity(Activity activity) {
return ((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(
0).getView();
}
private static void dumpViewRecursive(View view, int layer, int index, int total) {
if (view instanceof ViewGroup) {
Log.i(TAG, "L[" + layer + "] PARENT -> " + (index + 1) + "/" + total + " >> "
+ view.toString());
System.out.println(
TAG + " L[" + layer + "] PARENT -> " + (index + 1) + "/" + total + " >> "
+ view.toString());
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
dumpViewRecursive(((ViewGroup) view).getChildAt(i), layer + 1, i + 1,
((ViewGroup) view).getChildCount());
}
} else {
Log.i(TAG, "L[" + layer + "] =END= -> " + (index + 1) + "/" + total + " >> "
+ view.toString());
}
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2021 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.users;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.UserManager;
import android.util.Log;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.settings.Settings;
import com.android.settings.testutils.AdbUtils;
import com.android.settings.testutils.UiUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class UserSettingsComponentTest {
public static final int TIMEOUT = 2000;
private static final int USER_TYPE_RESTRICTED_PROFILE = 2;
public final String TAG = this.getClass().getName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final ArrayList<Integer> mOriginUserIds = new ArrayList<>();
private final UserManager mUserManager =
(UserManager) mInstrumentation.getTargetContext().getSystemService("user");
@Rule
public ActivityScenarioRule<Settings.UserSettingsActivity>
rule = new ActivityScenarioRule<>(
new Intent(android.provider.Settings.ACTION_USER_SETTINGS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
@Before
public void setUp() {
for (UserInfo info : mUserManager.getUsers()) {
mOriginUserIds.add(info.id);
}
// Enable multiple user switch.
if (!mUserManager.isUserSwitcherEnabled()) {
android.provider.Settings.Global.putInt(
mInstrumentation.getTargetContext().getContentResolver(),
android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1);
}
}
@Test
public void test_new_user_on_multiple_setting_page() throws IOException {
String randomUserName = gendrate_random_name(10);
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
Fragment f =
((FragmentActivity) activity).getSupportFragmentManager().getFragments().get(0);
UserSettings us = (UserSettings) f;
Log.d(TAG, "Start to add user :" + randomUserName);
us.createUser(USER_TYPE_RESTRICTED_PROFILE, randomUserName);
});
assertThat(
UiUtils.waitUntilCondition(5000, () -> mUserManager.getAliveUsers().stream().filter(
(user) -> user.name.equals(
randomUserName)).findFirst().isPresent())).isTrue();
}
@After
public void tearDown() {
int retryNumber = 5;
for (int i = 0; i < retryNumber; ++i) {
int currentUsersCount = mUserManager.getUserCount();
if (currentUsersCount == mOriginUserIds.size()) {
break;
} else if (i != 0) {
Log.d(TAG, "[tearDown] User not fully removed. Retry #" + (i = 1) + " of total "
+ mOriginUserIds.size());
}
for (UserInfo info : mUserManager.getUsers()) {
if (mOriginUserIds.contains(info.id)) {
continue;
}
Log.d(TAG, "[tearDown] Clean up user {" + info.id + "}:" + info.name);
try {
AdbUtils.shell("pm remove-user " + info.id);
} catch (Exception e) {
Log.w(TAG, "[tearDown] Error occurs while removing user. " + e.toString());
}
}
}
}
private String gendrate_random_name(int length) {
String seed = "abcdefghijklmnopqrstuvwxyABCDEFGHIJKLMNOPQSTUVWXYZ";
Random r1 = new Random();
String result = "";
for (int i = 0; i < length; ++i) {
result = result + seed.charAt(r1.nextInt(seed.length() - 1));
}
if (mUserManager.getAliveUsers().stream().map(user -> user.name).collect(
Collectors.toList()).contains(result)) {
Log.d(TAG, "Name repeated! add padding 'rpt' in the end of name.");
result += "rpt";
}
return result;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2021 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.wifi;
import static com.google.common.truth.Truth.assertThat;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
import com.android.settings.R;
import com.android.settings.Settings.NetworkProviderSettingsActivity;
import com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceControllerComponentTest;
import com.android.settings.network.NetworkProviderSettings;
import com.android.settings.testutils.CommonUtils;
import com.android.settings.testutils.Constants;
import com.android.settings.testutils.UiUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.URL;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@MediumTest
/*
This test is just for demonstration purpose. For component tests, this approach is not recommended.
The reason why it is written this way is because the current Settings app wifi codes have tight
coupling with UI, so it's not easy to drive from API without binding the test deeply with the code.
*/
public class WifiSettings2ActivityTest {
private static final String TAG =
BatterySaverButtonPreferenceControllerComponentTest.class.getSimpleName();
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@Test @Ignore
public void test_connect_to_wifi() throws Exception {
//For some reason the ActivityScenario gets null activity here
mInstrumentation.getTargetContext().startActivity(
new Intent(Settings.ACTION_WIFI_SETTINGS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
UiUtils.waitForActivitiesInStage(Constants.ACTIVITY_LAUNCH_WAIT_TIMEOUT, Stage.RESUMED);
final NetworkProviderSettings[] settings = new NetworkProviderSettings[1];
mInstrumentation.runOnMainSync(() -> {
NetworkProviderSettingsActivity activity = (NetworkProviderSettingsActivity)
ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
Stage.RESUMED).iterator().next();
settings[0] =
(NetworkProviderSettings) activity.getSupportFragmentManager().getFragments()
.get(0);
});
//For some reason this view does not appear immediately after the fragment is resumed.
View root = settings[0].getView();
UiUtils.waitUntilCondition(Constants.VIEW_APPEAR_WAIT_MEDIUM_TIMEOUT,
() -> root.findViewById(R.id.settings_button) != null);
View view = root.findViewById(R.id.settings_button);
view.callOnClick();
UiUtils.waitForActivitiesInStage(Constants.ACTIVITY_LAUNCH_WAIT_TIMEOUT, Stage.RESUMED);
Button[] button = new Button[1];
mInstrumentation.runOnMainSync(() -> {
FragmentActivity activity =
(FragmentActivity) ActivityLifecycleMonitorRegistry.getInstance()
.getActivitiesInStage(Stage.RESUMED).iterator().next();
List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
Log.d(TAG, "fragment class is " + fragments.get(0).getClass());
button[0] = fragments.get(0).getView().findViewById(R.id.button3);
});
//HttpURLConnection needs to run outside of main thread, so running it in the test thread
final URL url = new URL("https://www.google.net/");
//Make sure the connectivity is available before disconnecting from wifi
assertThat(CommonUtils.connectToURL(url)).isTrue();
//Disconnect from wifi
button[0].callOnClick();
//Make sure the Internet connectivity is gone
assertThat(CommonUtils.connectToURL(url)).isFalse();
//Connect to wifi
button[0].callOnClick();
ConnectivityManager manager =
(ConnectivityManager) mInstrumentation.getTargetContext().getSystemService(
Context.CONNECTIVITY_SERVICE);
//For some reason I can't find a way to tell the time that the internet connectivity is
//actually available with the new, non-deprecated ways, so I still need to use this.
UiUtils.waitUntilCondition(Constants.WIFI_CONNECT_WAIT_TIMEOUT,
() -> manager.getActiveNetworkInfo().isConnected());
//Make sure the connectivity is back again
assertThat(CommonUtils.connectToURL(url)).isTrue();
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.wifi.dpp;
import static android.app.Activity.RESULT_OK;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static com.google.common.truth.Truth.assertThat;
import static org.hamcrest.Matchers.equalTo;
import android.app.KeyguardManager;
import androidx.activity.result.ActivityResult;
import androidx.test.core.app.ActivityScenario;
import androidx.test.espresso.intent.Intents;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class WifiDppConfiguratorAuthActivityTest {
@Before
public void setup() {
Intents.init();
}
@After
public void teardown() throws Exception {
Intents.release();
}
@Test
public void launchActivity_sendAuthIntent() {
ActivityScenario<WifiDppConfiguratorAuthActivity> activityScenario =
ActivityScenario.launch(WifiDppConfiguratorAuthActivity.class);
assertThat(activityScenario).isNotNull();
intended(hasAction(equalTo(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER)));
}
@Test
public void launchActivity_sendQrCodeIntent() {
ActivityScenario.launch(WifiDppConfiguratorAuthActivity.class).onActivity(activity ->
activity.onAuthResult(new ActivityResult(RESULT_OK, /* data= */ null))
);
intended(hasAction(
equalTo(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR)));
}
@Test
public void launchActivity_shouldFinish() {
ActivityScenario.launch(WifiDppConfiguratorAuthActivity.class).onActivity(activity -> {
activity.onAuthResult(new ActivityResult(RESULT_OK, /* data= */ null));
assertThat(activity.isFinishing()).isTrue();
});
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2021 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.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settings.Settings;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ApplicationStateComponentTest {
private static final String TAG =
ApplicationStateComponentTest.class.getSimpleName();
private Context mRuntimeApplication;
private ApplicationsState mApplicationsState;
@Rule
public ActivityScenarioRule<Settings.ManageApplicationsActivity> rule =
new ActivityScenarioRule<>(
new Intent(
android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
private ApplicationsState.AppEntry createAppEntry(String label, String packageName, int id) {
ApplicationInfo appInfo = createApplicationInfo(packageName, id);
ApplicationsState.AppEntry appEntry = new ApplicationsState.AppEntry(mRuntimeApplication,
appInfo, id);
appEntry.label = label;
appEntry.mounted = true;
return appEntry;
}
private ApplicationInfo createApplicationInfo(String packageName, int uid) {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.sourceDir = "foo";
appInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
appInfo.storageUuid = UUID.randomUUID();
appInfo.packageName = packageName;
appInfo.uid = uid;
return appInfo;
}
@Test
public void test_all_apps_sorting_alpha() {
// TODO: Potential unit test candidate.
// To test all app list has sorted alphabetical, only need to verify sort function.
// This case focus on logic in sort function, and ignore origin locale sorting rule by Java.
ActivityScenario scenario = rule.getScenario();
scenario.onActivity(activity -> {
mRuntimeApplication = activity.getApplication();
mApplicationsState = ApplicationsState.getInstance(activity.getApplication());
ApplicationsState.AppEntry entry1 = createAppEntry("Info01", "Package1", 0);
ApplicationsState.AppEntry entry2 = createAppEntry("Info02", "Package1", 0);
ApplicationsState.AppEntry entry3 = createAppEntry("Info01", "Package2", 0);
ApplicationsState.AppEntry entry4 = createAppEntry("Info02", "Package2", 0);
ApplicationsState.AppEntry entry5 = createAppEntry("Info02", "Package2", 1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry1, entry2)).isEqualTo(-1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry2, entry3)).isEqualTo(1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry3, entry2)).isEqualTo(-1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry3, entry3)).isEqualTo(0);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry1, entry3)).isEqualTo(-1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry4, entry5)).isEqualTo(-1);
assertThat(ApplicationsState.ALPHA_COMPARATOR.compare(entry5, entry3)).isEqualTo(1);
});
}
}