diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java index 77827867630..754f99d3254 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 androidx.annotation.VisibleForTesting; 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 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo 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 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo 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); + } + } + + if (mIsWifiTrackerLib) { + mDialog2.show(); + mDialog2.setOnDismissListener(this); + } else { + mDialog.show(); + mDialog.setOnDismissListener(this); } - 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 @@ -133,6 +244,27 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo finish(); } + @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(); @@ -171,10 +303,20 @@ public class WifiDialogActivity extends Activity implements WifiDialog.WifiDialo @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); 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 00000000000..5df9ba52603 --- /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 41d1bbe4f38..0e1ca927162 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 static org.mockito.Mockito.doReturn; 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 @@ import org.robolectric.util.ReflectionHelpers; @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 mWifiDialogActivity; @Before public void setUp() { @@ -59,6 +69,18 @@ public class WifiDialogActivityTest { 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 createTargetActivity(Intent activityIntent) { + return ActivityScenario.launch(activityIntent); } @Test @@ -74,6 +96,27 @@ public class WifiDialogActivityTest { assertThat(ShadowWifiManager.get().savedWifiConfig.SSID).isEqualTo(AP1_SSID); } + @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 = @@ -92,6 +135,27 @@ public class WifiDialogActivityTest { assertThat(ShadowWifiManager.get().savedWifiConfig).isNull(); } + @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 =