Merge "Metrics for user choices in storage wizard." into pi-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d115a40..9beec69 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1863,39 +1863,39 @@
 
         <!-- Exported for SystemUI to launch into -->
         <activity android:name=".deviceinfo.StorageWizardInit"
-                android:theme="@style/GlifTheme.Light"
+                android:theme="@style/GlifV3Theme.Light"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="true"
                 android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
         <activity android:name=".deviceinfo.StorageWizardFormatProgress"
-                android:theme="@style/GlifTheme.Light"
+                android:theme="@style/GlifV3Theme.Light"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardFormatSlow"
-                android:theme="@style/GlifTheme.Light"
+                android:theme="@style/GlifV3Theme.Light"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardMigrateConfirm"
-                android:theme="@style/GlifTheme.Light"
+                android:theme="@style/GlifV3Theme.Light"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardMigrateProgress"
-                android:theme="@style/GlifTheme.Light"
+                android:theme="@style/GlifV3Theme.Light"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="true"
                 android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
         <activity android:name=".deviceinfo.StorageWizardReady"
-                android:theme="@style/GlifTheme.Light"
+                android:theme="@style/GlifV3Theme.Light"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="true"
                 android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
 
         <activity android:name=".deviceinfo.StorageWizardMoveConfirm"
-                android:theme="@style/GlifTheme.Light"
+                android:theme="@style/GlifV3Theme.Light"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardMoveProgress"
-                android:theme="@style/GlifTheme.Light"
+                android:theme="@style/GlifV3Theme.Light"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="true"
                 android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
diff --git a/res/drawable/ic_storage_wizard_external.xml b/res/drawable/ic_storage_wizard_external.xml
index c11334d..99e2698 100644
--- a/res/drawable/ic_storage_wizard_external.xml
+++ b/res/drawable/ic_storage_wizard_external.xml
@@ -19,71 +19,75 @@
         android:viewportWidth="144.0"
         android:viewportHeight="144.0">
     <path
-        android:pathData="M20.98,105C19.88,105 19,104.12 19,103.03L19,44.97C19,43.88 19.88,43 20.98,43L49.02,43C50.12,43 51,43.88 51,44.97L51,103.03C51,104.12 50.12,105 49.02,105L20.98,105Z"
+        android:pathData="M64,64m-64,0a64,64 0,1 1,128 0a64,64 0,1 1,-128 0"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="#FFFFFF"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M20.98,106C19.33,106 18,104.68 18,103.03L18,44.97C18,43.32 19.33,42 20.98,42L49.02,42C50.67,42 52,43.32 52,44.97L52,103.03C52,104.68 50.67,106 49.02,106L20.98,106Z"
+        android:pathData="M18.98,95C17.88,95 17,94.12 17,93.03L17,34.97C17,33.88 17.88,33 18.98,33L47.02,33C48.12,33 49,33.88 49,34.97L49,93.03C49,94.12 48.12,95 47.02,95L18.98,95Z"
+        android:strokeColor="#00000000"
+        android:fillType="evenOdd"
+        android:fillColor="#FFFFFF"
+        android:strokeWidth="1"/>
+    <path
+        android:pathData="M18.98,96C17.33,96 16,94.68 16,93.03L16,34.97C16,33.32 17.33,32 18.98,32L47.02,32C48.67,32 50,33.32 50,34.97L50,93.03C50,94.68 48.67,96 47.02,96L18.98,96Z"
         android:fillType="nonZero"
         android:strokeColor="#DADCE0"
         android:fillColor="#00000000"
         android:strokeWidth="2"/>
     <path
-        android:pathData="M20.98,104L49.02,104C49.57,104 50,103.57 50,103.03L50,44.97C50,44.43 49.57,44 49.02,44L20.98,44C20.43,44 20,44.43 20,44.97L20,103.03C20,103.57 20.43,104 20.98,104Z"
+        android:pathData="M18.98,94L47.02,94C47.57,94 48,93.57 48,93.03L48,34.97C48,34.43 47.57,34 47.02,34L18.98,34C18.43,34 18,34.43 18,34.97L18,93.03C18,93.57 18.43,94 18.98,94Z"
         android:fillType="nonZero"
         android:strokeColor="#F1F3F4"
         android:fillColor="#00000000"
         android:strokeWidth="2"/>
     <path
-        android:pathData="M79,97L159,97A1,1 0,0 1,160 98L160,106A1,1 0,0 1,159 107L79,107A1,1 0,0 1,78 106L78,98A1,1 0,0 1,79 97z"
+        android:pathData="M73,87L123.02,87L118.03,97L73,97C72.45,97 72,96.55 72,96L72,88C72,87.45 72.45,87 73,87Z"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="#F1F3F4"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M80,98L158,98A1,1 0,0 1,159 99L159,105A1,1 0,0 1,158 106L80,106A1,1 0,0 1,79 105L79,99A1,1 0,0 1,80 98z"
-        android:fillType="evenOdd"
-        android:strokeColor="#DADCE0"
-        android:fillColor="#00000000"
-        android:strokeWidth="2"/>
+        android:pathData="M74,89L74,95L119.11,95L121.99,89L74,89ZM73,87L122.59,87C122.26,87.73 120.83,90.87 118.29,96.42C118.13,96.77 117.77,97 117.38,97L73,97C72.45,97 72,96.55 72,96L72,88C72,87.45 72.45,87 73,87Z"
+        android:strokeColor="#00000000"
+        android:fillType="nonZero"
+        android:fillColor="#DADCE0"
+        android:strokeWidth="1"/>
     <path
-        android:pathData="M95,37L173,37C174.1,37 175,37.9 175,39L175,97C175,98.1 174.1,99 173,99L93,99L93,39C93,37.9 93.9,37 95,37Z"
+        android:pathData="M89,27L115.06,27C116.17,27 127.49,47.66 127.49,60.45C127.49,75.69 123.39,87.18 122.57,89L87,89L87,29C87,27.9 87.9,27 89,27Z"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="#F1F3F4"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M94,98L173,98C173.55,98 174,97.55 174,97L174,39C174,38.45 173.55,38 173,38L95,38C94.45,38 94,38.45 94,39L94,98Z"
-        android:fillType="evenOdd"
-        android:strokeColor="#DADCE0"
-        android:fillColor="#00000000"
-        android:strokeWidth="2"/>
+        android:pathData="M89,87L122.96,87C126.83,74.93 128.26,64.56 127.26,55.89C126.26,47.22 122.76,38.26 116.78,29L89,29L89,87ZM89,27L115.53,27C116.63,27 128.29,48.2 127.36,60.95C125.65,84.63 123.1,89 122,89L87,89L87,29C87,27.9 87.9,27 89,27Z"
+        android:strokeColor="#00000000"
+        android:fillType="nonZero"
+        android:fillColor="#DADCE0"
+        android:strokeWidth="1"/>
     <path
-        android:pathData="M100,43L180,43A1,1 0,0 1,181 44L181,92A1,1 0,0 1,180 93L100,93A1,1 0,0 1,99 92L99,44A1,1 0,0 1,100 43z"
+        android:pathData="M94,33L119.52,33C119.9,33 120.26,33.23 120.42,33.58C125.14,43.7 127.67,51.84 128,58C128.33,64.11 127.47,72.18 125.4,82.2L125.4,82.2C125.3,82.67 124.9,83 124.42,83L94,83C93.45,83 93,82.55 93,82L93,34C93,33.45 93.45,33 94,33C94,33 94,33 94,33Z"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="#FFFFFF"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M76.63,54L71.13,54L67.01,58.2L67,66.6C67,67.37 67.62,68 68.38,68L76.63,68C77.38,68 78,67.37 78,66.6L78,55.4C78,54.63 77.38,54 76.63,54ZM72.5,58.2L71.13,58.2L71.13,55.4L72.5,55.4L72.5,58.2ZM74.56,58.2L73.19,58.2L73.19,55.4L74.56,55.4L74.56,58.2ZM76.63,58.2L75.25,58.2L75.25,55.4L76.63,55.4L76.63,58.2Z"
+        android:pathData="M73.63,41L68.13,41L64.01,45.2L64,53.6C64,54.37 64.62,55 65.38,55L73.63,55C74.38,55 75,54.37 75,53.6L75,42.4C75,41.63 74.38,41 73.63,41ZM69.5,45.2L68.13,45.2L68.13,42.4L69.5,42.4L69.5,45.2ZM71.56,45.2L70.19,45.2L70.19,42.4L71.56,42.4L71.56,45.2ZM73.63,45.2L72.25,45.2L72.25,42.4L73.63,42.4L73.63,45.2Z"
         android:strokeColor="#00000000"
         android:fillType="nonZero"
-        android:fillAlpha="0.87"
         android:fillColor="?android:attr/colorAccent"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M66.99,82L63,86L66.99,90L66.99,87L74,87L74,85L66.99,85L66.99,82ZM81,80L77.01,76L77.01,79L70,79L70,81L77.01,81L77.01,84L81,80Z"
+        android:pathData="M63.99,69L60,73L63.99,77L63.99,74L71,74L71,72L63.99,72L63.99,69ZM78,67L74.01,63L74.01,66L67,66L67,68L74.01,68L74.01,71L78,67Z"
         android:strokeColor="#00000000"
         android:fillType="nonZero"
-        android:fillAlpha="0.87"
         android:fillColor="?android:attr/colorAccent"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M72,72m-71.04,0a71.04,71.04 0,1 1,142.08 0a71.04,71.04 0,1 1,-142.08 0"
-        android:fillType="evenOdd"
-        android:strokeColor="#DADCE0"
-        android:fillColor="#00000000"
-        android:strokeWidth="1.92"/>
+        android:pathData="M64,126.08C98.29,126.08 126.08,98.29 126.08,64C126.08,29.71 98.29,1.92 64,1.92C29.71,1.92 1.92,29.71 1.92,64C1.92,98.29 29.71,126.08 64,126.08ZM64,128C28.65,128 0,99.35 0,64C0,28.65 28.65,0 64,0C99.35,0 128,28.65 128,64C128,99.35 99.35,128 64,128Z"
+        android:strokeColor="#00000000"
+        android:fillType="nonZero"
+        android:fillColor="#DADCE0"
+        android:strokeWidth="1"/>
 </vector>
diff --git a/res/drawable/ic_storage_wizard_internal.xml b/res/drawable/ic_storage_wizard_internal.xml
index f6c660b..41768f7 100644
--- a/res/drawable/ic_storage_wizard_internal.xml
+++ b/res/drawable/ic_storage_wizard_internal.xml
@@ -14,60 +14,60 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="82dp"
-        android:height="150dp"
-        android:viewportWidth="82.0"
-        android:viewportHeight="150.0">
+        android:width="144dp"
+        android:height="144dp"
+        android:viewportWidth="144.0"
+        android:viewportHeight="144.0">
     <path
-        android:pathData="M42,62L45,62L45,64L42,64L42,67L40,67L40,64L37,64L37,62L40,62L40,59L42,59L42,62Z"
-        android:fillAlpha="0.87"
+        android:pathData="M28,4h72v140h-72z"
+        android:strokeColor="#00000000"
+        android:fillType="evenOdd"
+        android:fillColor="#FFFFFF"
+        android:strokeWidth="1"/>
+    <path
+        android:pathData="M65,62L68,62L68,64L65,64L65,67L63,67L63,64L60,64L60,62L63,62L63,59L65,59L65,62Z"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="?android:attr/colorAccent"
-        android:strokeWidth="1"
-        android:strokeAlpha="0.87"/>
+        android:strokeWidth="1"/>
     <path
-        android:pathData="M58.63,56L53.13,56L49.01,60.2L49,68.6C49,69.37 49.62,70 50.38,70L58.63,70C59.38,70 60,69.37 60,68.6L60,57.4C60,56.63 59.38,56 58.63,56ZM54.5,60.2L53.13,60.2L53.13,57.4L54.5,57.4L54.5,60.2ZM56.56,60.2L55.19,60.2L55.19,57.4L56.56,57.4L56.56,60.2ZM58.63,60.2L57.25,60.2L57.25,57.4L58.63,57.4L58.63,60.2Z"
-        android:fillAlpha="0.87"
+        android:pathData="M81.63,56L76.13,56L72.01,60.2L72,68.6C72,69.37 72.62,70 73.38,70L81.63,70C82.38,70 83,69.37 83,68.6L83,57.4C83,56.63 82.38,56 81.63,56ZM77.5,60.2L76.13,60.2L76.13,57.4L77.5,57.4L77.5,60.2ZM79.56,60.2L78.19,60.2L78.19,57.4L79.56,57.4L79.56,60.2ZM81.63,60.2L80.25,60.2L80.25,57.4L81.63,57.4L81.63,60.2Z"
         android:strokeColor="#00000000"
         android:fillType="nonZero"
         android:fillColor="?android:attr/colorAccent"
-        android:strokeWidth="1"
-        android:strokeAlpha="0.87"/>
+        android:strokeWidth="1"/>
     <path
-        android:pathData="M16,77L64,77A4,4 0,0 1,68 81L68,81A4,4 0,0 1,64 85L16,85A4,4 0,0 1,12 81L12,81A4,4 0,0 1,16 77z"
-        android:fillAlpha="0.87"
+        android:pathData="M39,77L87,77A4,4 0,0 1,91 81L91,81A4,4 0,0 1,87 85L39,85A4,4 0,0 1,35 81L35,81A4,4 0,0 1,39 77z"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="?android:attr/colorAccent"
-        android:strokeWidth="1"
-        android:strokeAlpha="0.87"/>
+        android:strokeWidth="1"/>
     <path
-        android:pathData="M9,77h31v8h-31z"
+        android:pathData="M32,77h31v8h-31z"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="#DADCE0"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M6,144L74,144L74,6L6,6L6,144ZM4.95,150C2.21,150 0,147.88 0,145.24L0,4.76C0,2.12 2.21,0 4.95,0L75.05,0C77.79,0 80,2.12 80,4.76L80,145.24C80,147.88 77.79,150 75.05,150L4.95,150Z"
+        android:pathData="M29,144L97,144L97,6L29,6L29,144ZM27.95,150C25.21,150 23,147.88 23,145.24L23,4.76C23,2.12 25.21,0 27.95,0L98.05,0C100.79,0 103,2.12 103,4.76L103,145.24C103,147.88 100.79,150 98.05,150L27.95,150Z"
         android:strokeColor="#00000000"
         android:fillType="nonZero"
         android:fillColor="#F1F3F4"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M4.95,148L75.05,148C76.7,148 78,146.75 78,145.24L78,4.76C78,3.25 76.7,2 75.05,2L4.95,2C3.3,2 2,3.25 2,4.76L2,145.24C2,146.75 3.3,148 4.95,148ZM4.95,150C2.21,150 0,147.88 0,145.24L0,4.76C0,2.12 2.21,0 4.95,0L75.05,0C77.79,0 80,2.12 80,4.76L80,145.24C80,147.88 77.79,150 75.05,150L4.95,150Z"
+        android:pathData="M27.95,148L98.05,148C99.7,148 101,146.75 101,145.24L101,4.76C101,3.25 99.7,2 98.05,2L27.95,2C26.3,2 25,3.25 25,4.76L25,145.24C25,146.75 26.3,148 27.95,148ZM27.95,150C25.21,150 23,147.88 23,145.24L23,4.76C23,2.12 25.21,0 27.95,0L98.05,0C100.79,0 103,2.12 103,4.76L103,145.24C103,147.88 100.79,150 98.05,150L27.95,150Z"
         android:strokeColor="#00000000"
         android:fillType="nonZero"
         android:fillColor="#DADCE0"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M80,58L80,58C81.1,58 82,58.9 82,60L82,76C82,77.1 81.1,78 80,78L80,58Z"
+        android:pathData="M103,58L103,58C104.1,58 105,58.9 105,60L105,76C105,77.1 104.1,78 103,78L103,58Z"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="#DADCE0"
         android:strokeWidth="1"/>
     <path
-        android:pathData="M80,32L80,32C81.1,32 82,32.9 82,34L82,40C82,41.1 81.1,42 80,42L80,32Z"
+        android:pathData="M103,32L103,32C104.1,32 105,32.9 105,34L105,40C105,41.1 104.1,42 103,42L103,32Z"
         android:strokeColor="#00000000"
         android:fillType="evenOdd"
         android:fillColor="#DADCE0"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 28973d7..cc4b1c4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8838,7 +8838,7 @@
     <string name="android_version_pending_update_summary">Update available</string>
 
     <!-- Title for dialog displayed when user selects on a setting locked by an admin [CHAR LIMIT=30] -->
-    <string name="disabled_by_policy_title">Can’t change this setting</string>
+    <string name="disabled_by_policy_title">Action not allowed</string>
     <!-- Title for dialog displayed to tell user that changing volume was disallowed by an admin [CHAR LIMIT=50] -->
     <string name="disabled_by_policy_title_adjust_volume">Can’t change volume</string>
     <!-- Title for dialog displayed to tell user that outgoing calls were disabled by an admin [CHAR LIMIT=50] -->
diff --git a/src/com/android/settings/network/PrivateDnsPreferenceController.java b/src/com/android/settings/network/PrivateDnsPreferenceController.java
index 50224ca..47aa4dc 100644
--- a/src/com/android/settings/network/PrivateDnsPreferenceController.java
+++ b/src/com/android/settings/network/PrivateDnsPreferenceController.java
@@ -24,6 +24,10 @@
 import android.content.ContentResolver;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
@@ -31,6 +35,7 @@
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
@@ -38,6 +43,8 @@
 import com.android.settingslib.core.lifecycle.events.OnStop;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 
+import java.net.InetAddress;
+import java.util.List;
 
 public class PrivateDnsPreferenceController extends BasePreferenceController
         implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
@@ -50,12 +57,15 @@
 
     private final Handler mHandler;
     private final ContentObserver mSettingsObserver;
+    private final ConnectivityManager mConnectivityManager;
+    private LinkProperties mLatestLinkProperties;
     private Preference mPreference;
 
     public PrivateDnsPreferenceController(Context context) {
         super(context, KEY_PRIVATE_DNS_SETTINGS);
         mHandler = new Handler(Looper.getMainLooper());
         mSettingsObserver = new PrivateDnsSettingsObserver(mHandler);
+        mConnectivityManager = context.getSystemService(ConnectivityManager.class);
     }
 
     @Override
@@ -80,11 +90,17 @@
         for (Uri uri : SETTINGS_URIS) {
             mContext.getContentResolver().registerContentObserver(uri, false, mSettingsObserver);
         }
+        final Network defaultNetwork = mConnectivityManager.getActiveNetwork();
+        if (defaultNetwork != null) {
+            mLatestLinkProperties = mConnectivityManager.getLinkProperties(defaultNetwork);
+        }
+        mConnectivityManager.registerDefaultNetworkCallback(mNetworkCallback, mHandler);
     }
 
     @Override
     public void onStop() {
         mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
     }
 
     @Override
@@ -92,13 +108,23 @@
         final Resources res = mContext.getResources();
         final ContentResolver cr = mContext.getContentResolver();
         final String mode = PrivateDnsModeDialogPreference.getModeFromSettings(cr);
+        final LinkProperties lp = mLatestLinkProperties;
+        final List<InetAddress> dnses = (lp == null) ? null : lp.getValidatedPrivateDnsServers();
+        final boolean dnsesResolved = !ArrayUtils.isEmpty(dnses);
         switch (mode) {
             case PRIVATE_DNS_MODE_OFF:
                 return res.getString(R.string.private_dns_mode_off);
             case PRIVATE_DNS_MODE_OPPORTUNISTIC:
-                return res.getString(R.string.private_dns_mode_opportunistic);
+                // TODO (b/79122154) : create a string specifically for this, instead of
+                // hijacking a string from notifications. This is necessary at this time
+                // because string freeze is in the past and this string has the right
+                // content at this moment.
+                return dnsesResolved ? res.getString(R.string.switch_on_text)
+                        : res.getString(R.string.private_dns_mode_opportunistic);
             case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
-                return PrivateDnsModeDialogPreference.getHostnameFromSettings(cr);
+                return dnsesResolved
+                        ? PrivateDnsModeDialogPreference.getHostnameFromSettings(cr)
+                        : res.getString(R.string.private_dns_mode_provider_failure);
         }
         return "";
     }
@@ -111,8 +137,25 @@
         @Override
         public void onChange(boolean selfChange) {
             if (mPreference != null) {
-                PrivateDnsPreferenceController.this.updateState(mPreference);
+                updateState(mPreference);
             }
         }
     }
+
+    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+        @Override
+        public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
+            mLatestLinkProperties = lp;
+            if (mPreference != null) {
+                updateState(mPreference);
+            }
+        }
+        @Override
+        public void onLost(Network network) {
+            mLatestLinkProperties = null;
+            if (mPreference != null) {
+                updateState(mPreference);
+            }
+        }
+    };
 }
diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
index 83d4bd5..ce40ab6 100644
--- a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
@@ -24,17 +24,29 @@
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.withSettings;
 import static org.mockito.Mockito.when;
 
 import android.arch.lifecycle.LifecycleOwner;
 import android.content.Context;
 import android.content.ContentResolver;
 import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.os.Handler;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
@@ -46,22 +58,45 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowContentResolver;
+import org.robolectric.shadows.ShadowServiceManager;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 public class PrivateDnsPreferenceControllerTest {
 
     private final static String HOSTNAME = "dns.example.com";
+    private final static List<InetAddress> NON_EMPTY_ADDRESS_LIST;
+    static {
+        try {
+            NON_EMPTY_ADDRESS_LIST = Arrays.asList(
+                    InetAddress.getByAddress(new byte[] { 8, 8, 8, 8 }));
+        } catch (UnknownHostException e) {
+            throw new RuntimeException("Invalid hardcoded IP addresss: " + e);
+        }
+    }
 
     @Mock
     private PreferenceScreen mScreen;
     @Mock
+    private ConnectivityManager mConnectivityManager;
+    @Mock
+    private Network mNetwork;
+    @Mock
     private Preference mPreference;
+    @Captor
+    private ArgumentCaptor<NetworkCallback> mCallbackCaptor;
     private PrivateDnsPreferenceController mController;
     private Context mContext;
     private ContentResolver mContentResolver;
@@ -75,15 +110,41 @@
         mContext = spy(RuntimeEnvironment.application);
         mContentResolver = mContext.getContentResolver();
         mShadowContentResolver = Shadow.extract(mContentResolver);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+                .thenReturn(mConnectivityManager);
+        doNothing().when(mConnectivityManager).registerDefaultNetworkCallback(
+                mCallbackCaptor.capture(), nullable(Handler.class));
 
         when(mScreen.findPreference(anyString())).thenReturn(mPreference);
 
         mController = spy(new PrivateDnsPreferenceController(mContext));
+
         mLifecycleOwner = () -> mLifecycle;
         mLifecycle = new Lifecycle(mLifecycleOwner);
         mLifecycle.addObserver(mController);
     }
 
+    private void updateLinkProperties(LinkProperties lp) {
+        NetworkCallback nc = mCallbackCaptor.getValue();
+        // The network callback that has been captured by the captor is the `mNetworkCallback'
+        // member of mController. mController being a spy, it has copied that member from the
+        // original object it was spying on, which means the object returned by the captor
+        // has a reference to the original object instead of the mock as its outer instance
+        // and will call methods and modify members of the original object instead of the spy,
+        // so methods subsequently called on the spy will not be aware of the changes. To work
+        // around this, the following code will create a new instance of the same class with
+        // the same code, but it sets the spy as the outer instance.
+        // A more recent version of Mockito would have made possible to create the spy with
+        // spy(PrivateDnsPreferenceController.class, withSettings().useConstructor(mContext))
+        // and that would have solved the problem by removing the original object entirely
+        // in a more elegant manner, but useConstructor(Object...) is only available starting
+        // with Mockito 2.7.14. Other solutions involve modifying the code under test for
+        // the sake of the test.
+        nc = mock(nc.getClass(), withSettings().useConstructor().outerInstance(mController)
+                .defaultAnswer(CALLS_REAL_METHODS));
+        nc.onLinkPropertiesChanged(mNetwork, lp);
+    }
+
     @Test
     public void goThroughLifecycle_shouldRegisterUnregisterSettingsObserver() {
         mLifecycle.handleLifecycleEvent(ON_START);
@@ -113,20 +174,50 @@
 
     @Test
     public void getSummary_PrivateDnsModeOpportunistic() {
+        mLifecycle.handleLifecycleEvent(ON_START);
         setPrivateDnsMode(PRIVATE_DNS_MODE_OPPORTUNISTIC);
         setPrivateDnsProviderHostname(HOSTNAME);
         mController.updateState(mPreference);
         verify(mController, atLeastOnce()).getSummary();
         verify(mPreference).setSummary(getResourceString(R.string.private_dns_mode_opportunistic));
+
+        LinkProperties lp = mock(LinkProperties.class);
+        when(lp.getValidatedPrivateDnsServers()).thenReturn(NON_EMPTY_ADDRESS_LIST);
+        updateLinkProperties(lp);
+        mController.updateState(mPreference);
+        verify(mPreference).setSummary(getResourceString(R.string.switch_on_text));
+
+        reset(mPreference);
+        lp = mock(LinkProperties.class);
+        when(lp.getValidatedPrivateDnsServers()).thenReturn(Collections.emptyList());
+        updateLinkProperties(lp);
+        mController.updateState(mPreference);
+        verify(mPreference).setSummary(getResourceString(R.string.private_dns_mode_opportunistic));
     }
 
     @Test
     public void getSummary_PrivateDnsModeProviderHostname() {
+        mLifecycle.handleLifecycleEvent(ON_START);
         setPrivateDnsMode(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
         setPrivateDnsProviderHostname(HOSTNAME);
         mController.updateState(mPreference);
         verify(mController, atLeastOnce()).getSummary();
+        verify(mPreference).setSummary(
+                getResourceString(R.string.private_dns_mode_provider_failure));
+
+        LinkProperties lp = mock(LinkProperties.class);
+        when(lp.getValidatedPrivateDnsServers()).thenReturn(NON_EMPTY_ADDRESS_LIST);
+        updateLinkProperties(lp);
+        mController.updateState(mPreference);
         verify(mPreference).setSummary(HOSTNAME);
+
+        reset(mPreference);
+        lp = mock(LinkProperties.class);
+        when(lp.getValidatedPrivateDnsServers()).thenReturn(Collections.emptyList());
+        updateLinkProperties(lp);
+        mController.updateState(mPreference);
+        verify(mPreference).setSummary(
+                getResourceString(R.string.private_dns_mode_provider_failure));
     }
 
     private void setPrivateDnsMode(String mode) {