[Wi-Fi] Support WifiTrackerLib in WifiDialogActivity
After this change, WifiDialogactivity supports both WifiTrackerLib and
SettingsLib because this is an exported UI component, some other APPs
(e.g., SetupWizard) still use SettingsLib.
Bug: 152571756
Test: make RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.wifi.WifiDialogActivityTest
manual
In Wi-Fi Settings, manually add a Wi-Fi network.
In SetupWizard, manually add a Wi-Fi network.
Change-Id: I87f477d905cc6ea34ef1afbfade3773920dad6eb
diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java
index 7782786..754f99d 100644
--- a/src/com/android/settings/wifi/WifiDialogActivity.java
+++ b/src/com/android/settings/wifi/WifiDialogActivity.java
@@ -16,14 +16,22 @@
package com.android.settings.wifi;
-import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
+import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import android.net.NetworkScoreManager;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.ActionListener;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.SimpleClock;
+import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -31,15 +39,33 @@
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.wifi.dpp.WifiDppUtils;
+import com.android.settingslib.core.lifecycle.ObservableActivity;
import com.android.settingslib.wifi.AccessPoint;
+import com.android.wifitrackerlib.NetworkDetailsTracker;
+import com.android.wifitrackerlib.WifiEntry;
import com.google.android.setupcompat.util.WizardManagerHelper;
-public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialogListener,
- DialogInterface.OnDismissListener {
+import java.time.Clock;
+import java.time.ZoneOffset;
+
+/**
+ * The activity shows a Wi-fi editor dialog.
+ *
+ * TODO(b/152571756): This activity supports both WifiTrackerLib and SettingsLib because this is an
+ * exported UI component, some other APPs (e.g., SetupWizard) still use
+ * SettingsLib. Remove the SettingsLib compatible part after these APPs use
+ * WifiTrackerLib.
+ */
+public class WifiDialogActivity extends ObservableActivity implements WifiDialog.WifiDialogListener,
+ WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener {
private static final String TAG = "WifiDialogActivity";
+ // For the callers which support WifiTrackerLib.
+ public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
+
+ // For the callers which support SettingsLib.
public static final String KEY_ACCESS_POINT_STATE = "access_point_state";
/**
@@ -58,9 +84,22 @@
private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0;
+ // Max age of tracked WifiEntries.
+ private static final long MAX_SCAN_AGE_MILLIS = 15_000;
+ // Interval between initiating NetworkDetailsTracker scans.
+ private static final long SCAN_INTERVAL_MILLIS = 10_000;
+
private WifiDialog mDialog;
+ private AccessPoint mAccessPoint;
+
+ private WifiDialog2 mDialog2;
+
+ // The received intent supports a key of WifiTrackerLib or SettingsLib.
+ private boolean mIsWifiTrackerLib;
private Intent mIntent;
+ private NetworkDetailsTracker mNetworkDetailsTracker;
+ private HandlerThread mWorkerThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -71,36 +110,108 @@
super.onCreate(savedInstanceState);
- final Bundle accessPointState = mIntent.getBundleExtra(KEY_ACCESS_POINT_STATE);
- AccessPoint accessPoint = null;
- if (accessPointState != null) {
- accessPoint = new AccessPoint(this, accessPointState);
+ mIsWifiTrackerLib = !TextUtils.isEmpty(mIntent.getStringExtra(KEY_CHOSEN_WIFIENTRY_KEY));
+
+ if (mIsWifiTrackerLib) {
+ mWorkerThread = new HandlerThread(
+ TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mWorkerThread.start();
+ final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
+ @Override
+ public long millis() {
+ return SystemClock.elapsedRealtime();
+ }
+ };
+ mNetworkDetailsTracker = NetworkDetailsTracker.createNetworkDetailsTracker(
+ getLifecycle(),
+ this,
+ getSystemService(WifiManager.class),
+ getSystemService(ConnectivityManager.class),
+ getSystemService(NetworkScoreManager.class),
+ new Handler(Looper.getMainLooper()),
+ mWorkerThread.getThreadHandler(),
+ elapsedRealtimeClock,
+ MAX_SCAN_AGE_MILLIS,
+ SCAN_INTERVAL_MILLIS,
+ mIntent.getStringExtra(KEY_CHOSEN_WIFIENTRY_KEY));
+ } else {
+ final Bundle accessPointState = mIntent.getBundleExtra(KEY_ACCESS_POINT_STATE);
+ if (accessPointState != null) {
+ mAccessPoint = new AccessPoint(this, accessPointState);
+ }
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (mDialog2 != null || mDialog != null) {
+ return;
}
if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
- mDialog = WifiDialog.createModal(this, this, accessPoint,
- WifiConfigUiBase.MODE_CONNECT, R.style.SuwAlertDialogThemeCompat_Light);
+ if (mIsWifiTrackerLib) {
+ mDialog2 = WifiDialog2.createModal(this, this,
+ mNetworkDetailsTracker.getWifiEntry(),
+ WifiConfigUiBase2.MODE_CONNECT, R.style.SuwAlertDialogThemeCompat_Light);
+ } else {
+ mDialog = WifiDialog.createModal(this, this, mAccessPoint,
+ WifiConfigUiBase.MODE_CONNECT, R.style.SuwAlertDialogThemeCompat_Light);
+ }
} else {
- mDialog = WifiDialog.createModal(
- this, this, accessPoint, WifiConfigUiBase.MODE_CONNECT);
+ if (mIsWifiTrackerLib) {
+ mDialog2 = WifiDialog2.createModal(this, this,
+ mNetworkDetailsTracker.getWifiEntry(), WifiConfigUiBase2.MODE_CONNECT);
+ } else {
+ mDialog = WifiDialog.createModal(
+ this, this, mAccessPoint, WifiConfigUiBase.MODE_CONNECT);
+ }
}
- mDialog.show();
- mDialog.setOnDismissListener(this);
+
+ if (mIsWifiTrackerLib) {
+ mDialog2.show();
+ mDialog2.setOnDismissListener(this);
+ } else {
+ mDialog.show();
+ mDialog.setOnDismissListener(this);
+ }
}
@Override
public void finish() {
- super.finish();
overridePendingTransition(0, 0);
+
+ super.finish();
}
@Override
public void onDestroy() {
- super.onDestroy();
- if (mDialog != null && mDialog.isShowing()) {
- mDialog.dismiss();
- mDialog = null;
+ if (mIsWifiTrackerLib) {
+ if (mDialog2 != null && mDialog2.isShowing()) {
+ mDialog2.dismiss();
+ mDialog2 = null;
+ }
+ mWorkerThread.quit();
+ } else {
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
}
+
+ super.onDestroy();
+ }
+
+ @Override
+ public void onForget(WifiDialog2 dialog) {
+ final WifiEntry wifiEntry = dialog.getController().getWifiEntry();
+ if (wifiEntry != null && wifiEntry.canForget()) {
+ wifiEntry.forget(null /* callback */);
+ }
+
+ setResult(RESULT_FORGET);
+ finish();
}
@Override
@@ -134,6 +245,27 @@
}
@Override
+ public void onSubmit(WifiDialog2 dialog) {
+ final WifiEntry wifiEntry = dialog.getController().getWifiEntry();
+ final WifiConfiguration config = dialog.getController().getConfig();
+
+ if (getIntent().getBooleanExtra(KEY_CONNECT_FOR_CALLER, true)) {
+ if (config == null && wifiEntry != null && wifiEntry.canConnect()) {
+ wifiEntry.connect(null /* callback */);
+ } else {
+ getSystemService(WifiManager.class).connect(config, null /* listener */);
+ }
+ }
+
+ final Intent resultData = new Intent();
+ if (config != null) {
+ resultData.putExtra(KEY_WIFI_CONFIGURATION, config);
+ }
+ setResult(RESULT_CONNECTED, resultData);
+ finish();
+ }
+
+ @Override
public void onSubmit(WifiDialog dialog) {
final WifiConfiguration config = dialog.getController().getConfig();
final AccessPoint accessPoint = dialog.getController().getAccessPoint();
@@ -171,11 +303,21 @@
@Override
public void onDismiss(DialogInterface dialogInterface) {
+ mDialog2 = null;
mDialog = null;
finish();
}
@Override
+ public void onScan(WifiDialog2 dialog, String ssid) {
+ Intent intent = WifiDppUtils.getEnrolleeQrCodeScannerIntent(ssid);
+ WizardManagerHelper.copyWizardManagerExtras(mIntent, intent);
+
+ // Launch QR code scanner to join a network.
+ startActivityForResult(intent, REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER);
+ }
+
+ @Override
public void onScan(WifiDialog dialog, String ssid) {
Intent intent = WifiDppUtils.getEnrolleeQrCodeScannerIntent(ssid);
WizardManagerHelper.copyWizardManagerExtras(mIntent, intent);
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNetworkDetailsTracker.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNetworkDetailsTracker.java
new file mode 100644
index 0000000..5df9ba5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNetworkDetailsTracker.java
@@ -0,0 +1,60 @@
+/*
+ * 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.shadow;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkScoreManager;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+
+import com.android.wifitrackerlib.NetworkDetailsTracker;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.time.Clock;
+
+@Implements(NetworkDetailsTracker.class)
+public class ShadowNetworkDetailsTracker {
+
+ @Implementation
+ public static NetworkDetailsTracker createNetworkDetailsTracker(@NonNull Lifecycle lifecycle,
+ @NonNull Context context,
+ @NonNull WifiManager wifiManager,
+ @NonNull ConnectivityManager connectivityManager,
+ @NonNull NetworkScoreManager networkScoreManager,
+ @NonNull Handler mainHandler,
+ @NonNull Handler workerHandler,
+ @NonNull Clock clock,
+ long maxScanAgeMillis,
+ long scanIntervalMillis,
+ String key) {
+ return mock(NetworkDetailsTracker.class);
+ }
+
+ @Implementation
+ public WifiEntry getWifiEntry() {
+ return mock(WifiEntry.class);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
index 41d1bbe..0e1ca92 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiDialogActivityTest.java
@@ -23,13 +23,18 @@
import android.content.Intent;
import android.net.wifi.WifiConfiguration;
+import androidx.lifecycle.Lifecycle.State;
+import androidx.test.core.app.ActivityScenario;
+
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowConnectivityManager;
+import com.android.settings.testutils.shadow.ShadowNetworkDetailsTracker;
import com.android.settings.testutils.shadow.ShadowWifiManager;
import com.google.android.setupcompat.util.WizardManagerHelper;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,15 +47,20 @@
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
+ ShadowAlertDialogCompat.class,
ShadowConnectivityManager.class,
- ShadowWifiManager.class,
- ShadowAlertDialogCompat.class
+ ShadowNetworkDetailsTracker.class,
+ ShadowWifiManager.class
})
public class WifiDialogActivityTest {
private static final String AP1_SSID = "\"ap1\"";
@Mock
private WifiConfigController mController;
+ @Mock
+ private WifiConfigController2 mController2;
+
+ private ActivityScenario<WifiDialogActivity> mWifiDialogActivity;
@Before
public void setUp() {
@@ -59,6 +69,18 @@
WifiConfiguration wifiConfig = new WifiConfiguration();
wifiConfig.SSID = AP1_SSID;
doReturn(wifiConfig).when(mController).getConfig();
+ doReturn(wifiConfig).when(mController2).getConfig();
+ }
+
+ @After
+ public void cleanUp() {
+ if (mWifiDialogActivity != null) {
+ mWifiDialogActivity.close();
+ }
+ }
+
+ private ActivityScenario<WifiDialogActivity> createTargetActivity(Intent activityIntent) {
+ return ActivityScenario.launch(activityIntent);
}
@Test
@@ -75,6 +97,27 @@
}
@Test
+ public void onSubmit2_whenConnectForCallerIsTrue_shouldConnectToNetwork() {
+ final Intent intent = new Intent("com.android.settings.WIFI_DIALOG");
+ intent.putExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY, "FAKE_KEY");
+ intent.putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, true);
+ mWifiDialogActivity = createTargetActivity(intent);
+
+ mWifiDialogActivity.moveToState(State.CREATED);
+ mWifiDialogActivity.moveToState(State.STARTED);
+
+ WifiDialog2 dialog = (WifiDialog2) ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+
+ ReflectionHelpers.setField(dialog, "mController", mController2);
+
+ mWifiDialogActivity.onActivity(activity -> {
+ activity.onSubmit(dialog);
+ assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP1_SSID);
+ });
+ }
+
+ @Test
public void onSubmit_whenConnectForCallerIsFalse_shouldNotConnectToNetwork() {
WifiDialogActivity activity =
Robolectric.buildActivity(
@@ -93,6 +136,27 @@
}
@Test
+ public void onSubmit2_whenConnectForCallerIsFalse_shouldNotConnectToNetwork() {
+ final Intent intent = new Intent("com.android.settings.WIFI_DIALOG");
+ intent.putExtra(WifiDialogActivity.KEY_CHOSEN_WIFIENTRY_KEY, "FAKE_KEY");
+ intent.putExtra(WifiDialogActivity.KEY_CONNECT_FOR_CALLER, false);
+ mWifiDialogActivity = createTargetActivity(intent);
+
+ mWifiDialogActivity.moveToState(State.CREATED);
+ mWifiDialogActivity.moveToState(State.STARTED);
+
+ WifiDialog2 dialog = (WifiDialog2) ShadowAlertDialogCompat.getLatestAlertDialog();
+ assertThat(dialog).isNotNull();
+
+ ReflectionHelpers.setField(dialog, "mController", mController2);
+
+ mWifiDialogActivity.onActivity(activity -> {
+ activity.onSubmit(dialog);
+ assertThat(ShadowWifiManager.get().savedWifiConfig).isEqualTo(null);
+ });
+ }
+
+ @Test
public void onSubmit_whenLaunchInSetupFlow_shouldBeLightThemeForWifiDialog() {
WifiDialogActivity activity =
Robolectric.buildActivity(