[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(