Support "don't ask again" in the avoid bad wifi dialog.

This contains the following changes:

1. Make NETWORK_AVOID_BAD_WIFI a tristate: 0 means never avoid
   bad wifi, unset means prompt the user, 1 means always avoid.
2. Look at NETWORK_AVOID_BAD_WIFI only if the carrier restricts
   avoiding bad wifi (previously, we relied on the setting being
   null and defaulting to the value of the config variable).
3. Add an avoidUnvalidated bit to NetworkAgentInfo to track
   whether the user has requested switching away from this
   unvalidated network even though avoiding bad wifi is generally
   disabled. This is set to true when the user selects "switch"
   in the dialog without setting the "Don't ask again" checkbox.
4. Add a hidden setAvoidUnvalidated API to ConnectivityManager to
   set the avoidUnvalidated bit.
5. Additional unit test coverage.

Bug: 31075769
Change-Id: I1be60c3016c8095df3c4752330149ce638bd0ce1
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 59c8840..a0ef25e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -366,6 +366,11 @@
     private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28;
 
     /**
+     * used to specify whether a network should not be penalized when it becomes unvalidated.
+     */
+    private static final int EVENT_SET_AVOID_UNVALIDATED = 35;
+
+    /**
      * used to ask the user to confirm a connection to an unvalidated network.
      * obj  = network
      */
@@ -2681,6 +2686,12 @@
                 accept ? 1 : 0, always ? 1: 0, network));
     }
 
+    @Override
+    public void setAvoidUnvalidated(Network network) {
+        enforceConnectivityInternalPermission();
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_AVOID_UNVALIDATED, network));
+    }
+
     private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
         if (DBG) log("handleSetAcceptUnvalidated network=" + network +
                 " accept=" + accept + " always=" + always);
@@ -2721,6 +2732,20 @@
 
     }
 
+    private void handleSetAvoidUnvalidated(Network network) {
+        NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai == null || nai.lastValidated) {
+            // Nothing to do. The network either disconnected or revalidated.
+            return;
+        }
+        if (!nai.avoidUnvalidated) {
+            int oldScore = nai.getCurrentScore();
+            nai.avoidUnvalidated = true;
+            rematchAllNetworksAndRequests(nai, oldScore);
+            sendUpdatedScoreToFactories(nai);
+        }
+    }
+
     private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
         if (VDBG) log("scheduleUnvalidatedPrompt " + nai.network);
         mHandler.sendMessageDelayed(
@@ -2728,28 +2753,31 @@
                 PROMPT_UNVALIDATED_DELAY_MS);
     }
 
-    private boolean mAvoidBadWifi;
+    private boolean mAvoidBadWifi = true;
 
     public boolean avoidBadWifi() {
         return mAvoidBadWifi;
     }
 
     @VisibleForTesting
-    public boolean updateAvoidBadWifi() {
-        // There are two modes: either we always automatically avoid unvalidated wifi, or we show a
-        // dialog and don't switch to it. The behaviour is controlled by the NETWORK_AVOID_BAD_WIFI
-        // setting. If the setting has no value, then the value is taken from the config value,
-        // which can be changed via OEM/carrier overlays.
-        //
-        // The only valid values for NETWORK_AVOID_BAD_WIFI are null and unset. Currently, the unit
-        // test uses 0 in order to avoid having to mock out fetching the carrier setting.
-        int defaultAvoidBadWifi =
-            mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi);
-        int avoid = Settings.Global.getInt(mContext.getContentResolver(),
-            Settings.Global.NETWORK_AVOID_BAD_WIFI, defaultAvoidBadWifi);
+    /** Whether the device or carrier configuration disables avoiding bad wifi by default. */
+    public boolean configRestrictsAvoidBadWifi() {
+        return mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0;
+    }
+
+    /** Whether we should display a notification when wifi becomes unvalidated. */
+    public boolean shouldNotifyWifiUnvalidated() {
+        return configRestrictsAvoidBadWifi() &&
+                Settings.Global.getString(mContext.getContentResolver(),
+                        Settings.Global.NETWORK_AVOID_BAD_WIFI) == null;
+    }
+
+    private boolean updateAvoidBadWifi() {
+        boolean settingAvoidBadWifi = "1".equals(Settings.Global.getString(
+                mContext.getContentResolver(), Settings.Global.NETWORK_AVOID_BAD_WIFI));
 
         boolean prev = mAvoidBadWifi;
-        mAvoidBadWifi = (avoid == 1);
+        mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi();
         return mAvoidBadWifi != prev;
     }
 
@@ -2802,7 +2830,7 @@
         NetworkCapabilities nc = nai.networkCapabilities;
         if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
 
-        if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && !avoidBadWifi()) {
+        if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && shouldNotifyWifiUnvalidated()) {
             showValidationNotification(nai, NotificationType.LOST_INTERNET);
         }
     }
@@ -2880,6 +2908,10 @@
                     handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0);
                     break;
                 }
+                case EVENT_SET_AVOID_UNVALIDATED: {
+                    handleSetAvoidUnvalidated((Network) msg.obj);
+                    break;
+                }
                 case EVENT_PROMPT_UNVALIDATED: {
                     handlePromptUnvalidated((Network) msg.obj);
                     break;