OSDN Git Service

Display Private DNS status in preference summary
authorErik Kline <ek@google.com>
Thu, 5 Apr 2018 21:30:56 +0000 (14:30 -0700)
committerErik Kline <ek@google.com>
Sun, 8 Apr 2018 22:50:58 +0000 (15:50 -0700)
Also: fix a bug where the actual current mode is not read correctly
from settings on initialization.

Test: as follows
    - built, flashed, booted
    - manual test behaves as expected
    - make -j50 RunSettingsRoboTests ROBOTEST_FILTER=PrivateDnsModeDialogPreferenceTest passes
    - make -j50 RunSettingsRoboTests ROBOTEST_FILTER=PrivateDnsPreferenceControllerTest passes
Bug: 34953048
Bug: 64133961
Change-Id: I0b845b3894a47b837abf5de273d1ada642ef5a23

src/com/android/settings/network/PrivateDnsModeDialogPreference.java
src/com/android/settings/network/PrivateDnsPreferenceController.java
tests/robotests/src/com/android/settings/network/PrivateDnsModeDialogPreferenceTest.java
tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java [new file with mode: 0644]

index 71a3e0d..178aa27 100644 (file)
@@ -53,7 +53,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Dialog to set the private dns
+ * Dialog to set the Private DNS
  */
 public class PrivateDnsModeDialogPreference extends CustomDialogPreference implements
         DialogInterface.OnClickListener, RadioGroup.OnCheckedChangeListener, TextWatcher {
@@ -78,6 +78,15 @@ public class PrivateDnsModeDialogPreference extends CustomDialogPreference imple
     @VisibleForTesting
     static final String HOSTNAME_KEY = Settings.Global.PRIVATE_DNS_SPECIFIER;
 
+    public static String getModeFromSettings(ContentResolver cr) {
+        final String mode = Settings.Global.getString(cr, MODE_KEY);
+        return PRIVATE_DNS_MAP.containsKey(mode) ? mode : PRIVATE_DNS_MODE_OPPORTUNISTIC;
+    }
+
+    public static String getHostnameFromSettings(ContentResolver cr) {
+        return Settings.Global.getString(cr, HOSTNAME_KEY);
+    }
+
     @VisibleForTesting
     EditText mEditText;
     @VisibleForTesting
@@ -121,9 +130,12 @@ public class PrivateDnsModeDialogPreference extends CustomDialogPreference imple
     protected void onBindDialogView(View view) {
         final Context context = getContext();
         final ContentResolver contentResolver = context.getContentResolver();
+
+        mMode = getModeFromSettings(context.getContentResolver());
+
         mEditText = view.findViewById(R.id.private_dns_mode_provider_hostname);
         mEditText.addTextChangedListener(this);
-        mEditText.setText(Settings.Global.getString(contentResolver, HOSTNAME_KEY));
+        mEditText.setText(getHostnameFromSettings(contentResolver));
 
         mRadioGroup = view.findViewById(R.id.private_dns_radio_group);
         mRadioGroup.setOnCheckedChangeListener(this);
index e317530..50224ca 100644 (file)
 
 package com.android.settings.network;
 
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
 import android.content.Context;
+import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 
+
 public class PrivateDnsPreferenceController extends BasePreferenceController
-        implements PreferenceControllerMixin, LifecycleObserver {
+        implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
     private static final String KEY_PRIVATE_DNS_SETTINGS = "private_dns_settings";
 
+    private static final Uri[] SETTINGS_URIS = new Uri[]{
+        Settings.Global.getUriFor(Settings.Global.PRIVATE_DNS_MODE),
+        Settings.Global.getUriFor(Settings.Global.PRIVATE_DNS_SPECIFIER),
+    };
+
+    private final Handler mHandler;
+    private final ContentObserver mSettingsObserver;
+    private Preference mPreference;
+
     public PrivateDnsPreferenceController(Context context) {
         super(context, KEY_PRIVATE_DNS_SETTINGS);
+        mHandler = new Handler(Looper.getMainLooper());
+        mSettingsObserver = new PrivateDnsSettingsObserver(mHandler);
     }
 
     @Override
@@ -40,4 +67,52 @@ public class PrivateDnsPreferenceController extends BasePreferenceController
     public int getAvailabilityStatus() {
         return AVAILABLE;
     }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+
+        mPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    @Override
+    public void onStart() {
+        for (Uri uri : SETTINGS_URIS) {
+            mContext.getContentResolver().registerContentObserver(uri, false, mSettingsObserver);
+        }
+    }
+
+    @Override
+    public void onStop() {
+        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
+    public CharSequence getSummary() {
+        final Resources res = mContext.getResources();
+        final ContentResolver cr = mContext.getContentResolver();
+        final String mode = PrivateDnsModeDialogPreference.getModeFromSettings(cr);
+        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);
+            case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
+                return PrivateDnsModeDialogPreference.getHostnameFromSettings(cr);
+        }
+        return "";
+    }
+
+    private class PrivateDnsSettingsObserver extends ContentObserver {
+        public PrivateDnsSettingsObserver(Handler h) {
+            super(h);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (mPreference != null) {
+                PrivateDnsPreferenceController.this.updateState(mPreference);
+            }
+        }
+    }
 }
index c5521be..e9ffa8a 100644 (file)
@@ -111,8 +111,10 @@ public class PrivateDnsModeDialogPreferenceTest {
 
     @Test
     public void testOnBindDialogView_containsCorrectData() {
+        // Don't set settings to the default value ("opportunistic") as that
+        // risks masking failure to read the mode from settings.
         Settings.Global.putString(mContext.getContentResolver(),
-                PrivateDnsModeDialogPreference.MODE_KEY, PRIVATE_DNS_MODE_OPPORTUNISTIC);
+                PrivateDnsModeDialogPreference.MODE_KEY, PRIVATE_DNS_MODE_OFF);
         Settings.Global.putString(mContext.getContentResolver(),
                 PrivateDnsModeDialogPreference.HOSTNAME_KEY, HOST_NAME);
 
@@ -123,7 +125,7 @@ public class PrivateDnsModeDialogPreferenceTest {
 
         assertThat(mPreference.mEditText.getText().toString()).isEqualTo(HOST_NAME);
         assertThat(mPreference.mRadioGroup.getCheckedRadioButtonId()).isEqualTo(
-                R.id.private_dns_mode_opportunistic);
+                R.id.private_dns_mode_off);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
new file mode 100644 (file)
index 0000000..83d4bd5
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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.network;
+
+import static android.arch.lifecycle.Lifecycle.Event.ON_START;
+import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+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.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+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.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContentResolver;
+
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class PrivateDnsPreferenceControllerTest {
+
+    private final static String HOSTNAME = "dns.example.com";
+
+    @Mock
+    private PreferenceScreen mScreen;
+    @Mock
+    private Preference mPreference;
+    private PrivateDnsPreferenceController mController;
+    private Context mContext;
+    private ContentResolver mContentResolver;
+    private ShadowContentResolver mShadowContentResolver;
+    private Lifecycle mLifecycle;
+    private LifecycleOwner mLifecycleOwner;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        mContentResolver = mContext.getContentResolver();
+        mShadowContentResolver = Shadow.extract(mContentResolver);
+
+        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+
+        mController = spy(new PrivateDnsPreferenceController(mContext));
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        mLifecycle.addObserver(mController);
+    }
+
+    @Test
+    public void goThroughLifecycle_shouldRegisterUnregisterSettingsObserver() {
+        mLifecycle.handleLifecycleEvent(ON_START);
+        verify(mContext, atLeastOnce()).getContentResolver();
+        assertThat(mShadowContentResolver.getContentObservers(
+                Settings.Global.getUriFor(PRIVATE_DNS_MODE))).isNotEmpty();
+        assertThat(mShadowContentResolver.getContentObservers(
+                Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER))).isNotEmpty();
+
+
+        mLifecycle.handleLifecycleEvent(ON_STOP);
+        verify(mContext, atLeastOnce()).getContentResolver();
+        assertThat(mShadowContentResolver.getContentObservers(
+                Settings.Global.getUriFor(PRIVATE_DNS_MODE))).isEmpty();
+        assertThat(mShadowContentResolver.getContentObservers(
+                Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER))).isEmpty();
+    }
+
+    @Test
+    public void getSummary_PrivateDnsModeOff() {
+        setPrivateDnsMode(PRIVATE_DNS_MODE_OFF);
+        setPrivateDnsProviderHostname(HOSTNAME);
+        mController.updateState(mPreference);
+        verify(mController, atLeastOnce()).getSummary();
+        verify(mPreference).setSummary(getResourceString(R.string.private_dns_mode_off));
+    }
+
+    @Test
+    public void getSummary_PrivateDnsModeOpportunistic() {
+        setPrivateDnsMode(PRIVATE_DNS_MODE_OPPORTUNISTIC);
+        setPrivateDnsProviderHostname(HOSTNAME);
+        mController.updateState(mPreference);
+        verify(mController, atLeastOnce()).getSummary();
+        verify(mPreference).setSummary(getResourceString(R.string.private_dns_mode_opportunistic));
+    }
+
+    @Test
+    public void getSummary_PrivateDnsModeProviderHostname() {
+        setPrivateDnsMode(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+        setPrivateDnsProviderHostname(HOSTNAME);
+        mController.updateState(mPreference);
+        verify(mController, atLeastOnce()).getSummary();
+        verify(mPreference).setSummary(HOSTNAME);
+    }
+
+    private void setPrivateDnsMode(String mode) {
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_MODE, mode);
+    }
+
+    private void setPrivateDnsProviderHostname(String name) {
+        Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, name);
+    }
+
+    private String getResourceString(int which) {
+        return mContext.getResources().getString(which);
+    }
+}