Initial commit for Settings component test

Test: Tested on local device
Bug: 178765084
Change-Id: I3b8f36daa31b0a44e788fe4c84f94d48653ee037
diff --git a/tests/componenttests/Android.bp b/tests/componenttests/Android.bp
new file mode 100644
index 0000000..77932ef
--- /dev/null
+++ b/tests/componenttests/Android.bp
@@ -0,0 +1,23 @@
+//############################################################
+// Settings Component test target.                           #
+//############################################################
+android_test {
+    name: "SettingsComponentTests",
+    certificate: "platform",
+    privileged: true,
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "truth-prebuilt",
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+    ],
+
+    test_suites: ["device-tests"],
+
+    instrumentation_for: "Settings",
+}
diff --git a/tests/componenttests/AndroidManifest.xml b/tests/componenttests/AndroidManifest.xml
new file mode 100644
index 0000000..54ea374
--- /dev/null
+++ b/tests/componenttests/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?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">
+
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <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>
\ No newline at end of file
diff --git a/tests/componenttests/AndroidTest.xml b/tests/componenttests/AndroidTest.xml
new file mode 100644
index 0000000..ea7ac06
--- /dev/null
+++ b/tests/componenttests/AndroidTest.xml
@@ -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>
diff --git a/tests/componenttests/OWNERS b/tests/componenttests/OWNERS
new file mode 100644
index 0000000..51746f0
--- /dev/null
+++ b/tests/componenttests/OWNERS
@@ -0,0 +1,3 @@
+# People who can approve changes for submission
+jyhsu@google.com
+syaoranx@google.com
\ No newline at end of file
diff --git a/tests/componenttests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerComponentTest.java b/tests/componenttests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerComponentTest.java
new file mode 100644
index 0000000..30fcbf5
--- /dev/null
+++ b/tests/componenttests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerComponentTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Button;
+
+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.R;
+import com.android.settings.Settings.BatterySaverSettingsActivity;
+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;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BatterySaverButtonPreferenceControllerComponentTest {
+    private static final String TAG =
+            BatterySaverButtonPreferenceControllerComponentTest.class.getSimpleName();
+    private Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    private 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");
+    }
+
+    @Test
+    public void test_check_battery_saver_button() throws Exception {
+        ActivityScenario scenario = rule.getScenario();
+        scenario.onActivity(activity -> {
+            final Button button = activity.findViewById(R.id.state_on_button);
+            UiUtils.waitUntilCondition(3000, () -> button.isEnabled());
+            button.callOnClick();
+            checkPowerSaverMode(true);
+
+            Button offButton = activity.findViewById(R.id.state_off_button);
+            offButton.callOnClick();
+            checkPowerSaverMode(false);
+        });
+
+        //Ideally, we should be able to also create BatteryTipPreferenceController and verify that
+        //it is showing battery saver on. Unfortunately, that part of code is tightly coupled with
+        //UI, and it's not possible to retrieve that string without reaching very deep into the
+        //codes and become very tightly coupled with any future changes. That is not what component
+        //tests should do, so either we'll need to do this through UI with another ActivityScenario,
+        //or the code needs to be refactored to be less coupled with UI.
+    }
+
+    @Test
+    public void test_battery_saver_button_changes_when_framework_setting_change() throws Exception {
+        ActivityScenario scenario = rule.getScenario();
+        scenario.onActivity(activity -> {
+            Button buttonOn = activity.findViewById(R.id.state_on_button);
+            Button buttonOff = activity.findViewById(R.id.state_off_button);
+            assertThat(buttonOn.isVisibleToUser()).isEqualTo(true);
+            assertThat(buttonOff.isVisibleToUser()).isEqualTo(false);
+        });
+
+        mManager.setPowerSaveModeEnabled(true);
+        scenario.recreate();
+        scenario.onActivity(activity -> {
+            Button buttonOn = activity.findViewById(R.id.state_on_button);
+            Button buttonOff = activity.findViewById(R.id.state_off_button);
+            assertThat(buttonOn.isVisibleToUser()).isEqualTo(false);
+            assertThat(buttonOff.isVisibleToUser()).isEqualTo(true);
+        });
+
+        mManager.setPowerSaveModeEnabled(false);
+        scenario.recreate();
+        scenario.onActivity(activity -> {
+            Button buttonOn = activity.findViewById(R.id.state_on_button);
+            Button buttonOff = activity.findViewById(R.id.state_off_button);
+            assertThat(buttonOn.isVisibleToUser()).isEqualTo(true);
+            assertThat(buttonOff.isVisibleToUser()).isEqualTo(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();
+    }
+}
diff --git a/tests/componenttests/src/com/android/settings/privacy/EnabledContentCapturePreferenceControllerComponentTest.java b/tests/componenttests/src/com/android/settings/privacy/EnabledContentCapturePreferenceControllerComponentTest.java
new file mode 100644
index 0000000..aba6f8f
--- /dev/null
+++ b/tests/componenttests/src/com/android/settings/privacy/EnabledContentCapturePreferenceControllerComponentTest.java
@@ -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();
+    }
+}
diff --git a/tests/componenttests/src/com/android/settings/testutils/AdbUtils.java b/tests/componenttests/src/com/android/settings/testutils/AdbUtils.java
new file mode 100644
index 0000000..08eb47a
--- /dev/null
+++ b/tests/componenttests/src/com/android/settings/testutils/AdbUtils.java
@@ -0,0 +1,62 @@
+/*
+ * 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 android.text.TextUtils;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+
+public class AdbUtils {
+    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)) {
+            try (ParcelFileDescriptor.AutoCloseInputStream in =
+                         new ParcelFileDescriptor.AutoCloseInputStream(
+                                 InstrumentationRegistry.getInstrumentation()
+                                         .getUiAutomation()
+                                         .executeShellCommand(command))) {
+                try (BufferedReader br =
+                             new BufferedReader(
+                                     new InputStreamReader(in, StandardCharsets.UTF_8))) {
+                    Optional<String> resultOptional = br.lines().filter(line -> {
+                        Log.d(logTag, line);
+                        return TextUtils.isEmpty(prefix) || line.contains(prefix);
+                    }).findFirst();
+                    String result = resultOptional.get();
+                    if (result.contains(target)) {
+                        return true;
+                    } else {
+                        Thread.sleep(100);
+                    }
+                }
+            } catch (Exception e) {
+                throw e;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/tests/componenttests/src/com/android/settings/testutils/CommonUtils.java b/tests/componenttests/src/com/android/settings/testutils/CommonUtils.java
new file mode 100644
index 0000000..cbfe245
--- /dev/null
+++ b/tests/componenttests/src/com/android/settings/testutils/CommonUtils.java
@@ -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.testutils;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.os.Environment;
+import android.util.Log;
+import android.view.View;
+
+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();
+
+    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) {
+        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);
+                }
+                return true;
+            }
+        } catch (Exception e) {
+            Log.d(TAG, e.getMessage());
+            return false;
+        } finally {
+            if (null != connection) {
+                connection.disconnect();
+            }
+        }
+
+        return false;
+    }
+
+}
diff --git a/tests/componenttests/src/com/android/settings/testutils/Constants.java b/tests/componenttests/src/com/android/settings/testutils/Constants.java
new file mode 100644
index 0000000..5e32f87
--- /dev/null
+++ b/tests/componenttests/src/com/android/settings/testutils/Constants.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+}
diff --git a/tests/componenttests/src/com/android/settings/testutils/UiUtils.java b/tests/componenttests/src/com/android/settings/testutils/UiUtils.java
new file mode 100644
index 0000000..a482add
--- /dev/null
+++ b/tests/componenttests/src/com/android/settings/testutils/UiUtils.java
@@ -0,0 +1,58 @@
+/*
+ * 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 androidx.test.platform.app.InstrumentationRegistry;
+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 {
+
+    public static void 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;
+                }
+            } catch (NullPointerException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public static boolean waitForActivitiesInStage(long timeoutInMillis, Stage stage) {
+        final Collection<Activity> activities = new ArrayList<>();
+        waitUntilCondition(Constants.ACTIVITY_LAUNCH_WAIT_TIMEOUT, () -> {
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activities.addAll(
+                    ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
+                            Stage.RESUMED)));
+            return activities.size() > 0;
+        });
+
+        return activities.size() > 0;
+    }
+}
diff --git a/tests/componenttests/src/com/android/settings/wifi/WifiSettings2ActivityTest.java b/tests/componenttests/src/com/android/settings/wifi/WifiSettings2ActivityTest.java
new file mode 100644
index 0000000..aa6b252
--- /dev/null
+++ b/tests/componenttests/src/com/android/settings/wifi/WifiSettings2ActivityTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.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
+    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();
+    }
+}
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index c8c6c38..f6ad049 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -1,4 +1,4 @@
-//############################################################
+    //############################################################
 // Build SettingsRoboTestStub.apk which includes test-only resources.#
 //############################################################