Loading res/layout/private_dns_mode_dialog.xml +6 −9 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ <RadioGroup <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/private_dns_radio_group" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="wrap_content" android:padding="8dip"> android:padding="8dip"> Loading @@ -25,24 +26,21 @@ android:text="@string/private_dns_mode_off" android:text="@string/private_dns_mode_off" android:layout_width="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip" android:layout_margin="8dip"/> /> <RadioButton <RadioButton android:id="@+id/private_dns_mode_opportunistic" android:id="@+id/private_dns_mode_opportunistic" android:text="@string/private_dns_mode_opportunistic" android:text="@string/private_dns_mode_opportunistic" android:layout_width="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip" android:layout_margin="8dip"/> /> <RadioButton <RadioButton android:id="@+id/private_dns_mode_provider" android:id="@+id/private_dns_mode_provider" android:text="@string/private_dns_mode_provider" android:text="@string/private_dns_mode_provider" android:layout_width="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip" android:layout_margin="8dip"/> /> <EditText <EditText android:id="@+id/private_dns_mode_provider_hostname" android:id="@+id/private_dns_mode_provider_hostname" Loading @@ -52,8 +50,7 @@ android:inputType="textFilter|textUri" android:inputType="textFilter|textUri" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dip" android:layout_marginStart="40dip" android:layout_marginEnd="8dip" android:layout_marginEnd="8dip"/> /> </RadioGroup> </RadioGroup> src/com/android/settings/network/NetworkDashboardFragment.java +7 −1 Original line number Original line Diff line number Diff line Loading @@ -48,8 +48,11 @@ public class NetworkDashboardFragment extends DashboardFragment implements MobilePlanPreferenceHost { MobilePlanPreferenceHost { private static final String TAG = "NetworkDashboardFrag"; private static final String TAG = "NetworkDashboardFrag"; private static final int MENU_NETWORK_RESET = Menu.FIRST; private static final int MENU_PRIVATE_DNS = Menu.FIRST + 1; private NetworkResetActionMenuController mNetworkResetController; private NetworkResetActionMenuController mNetworkResetController; private PrivateDnsMenuController mPrivateDnsMenuController; @Override @Override public int getMetricsCategory() { public int getMetricsCategory() { Loading @@ -69,7 +72,9 @@ public class NetworkDashboardFragment extends DashboardFragment implements @Override @Override public void onAttach(Context context) { public void onAttach(Context context) { super.onAttach(context); super.onAttach(context); mNetworkResetController = new NetworkResetActionMenuController(context); mNetworkResetController = new NetworkResetActionMenuController(context, MENU_NETWORK_RESET); mPrivateDnsMenuController = new PrivateDnsMenuController(getFragmentManager(), MENU_PRIVATE_DNS); } } @Override @Override Loading @@ -81,6 +86,7 @@ public class NetworkDashboardFragment extends DashboardFragment implements public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater); mNetworkResetController.buildMenuItem(menu); mNetworkResetController.buildMenuItem(menu); mPrivateDnsMenuController.buildMenuItem(menu); } } @Override @Override Loading src/com/android/settings/network/NetworkResetActionMenuController.java +4 −3 Original line number Original line Diff line number Diff line Loading @@ -27,19 +27,20 @@ import com.android.settings.Utils; public class NetworkResetActionMenuController { public class NetworkResetActionMenuController { private static final int MENU_NETWORK_RESET = Menu.FIRST + 200; private final Context mContext; private final Context mContext; private final NetworkResetRestrictionChecker mRestrictionChecker; private final NetworkResetRestrictionChecker mRestrictionChecker; private final int mMenuId; public NetworkResetActionMenuController(Context context) { public NetworkResetActionMenuController(Context context, int menuId) { mContext = context; mContext = context; mRestrictionChecker = new NetworkResetRestrictionChecker(context); mRestrictionChecker = new NetworkResetRestrictionChecker(context); mMenuId = menuId; } } public void buildMenuItem(Menu menu) { public void buildMenuItem(Menu menu) { MenuItem item = null; MenuItem item = null; if (isAvailable() && menu != null) { if (isAvailable() && menu != null) { item = menu.add(0, MENU_NETWORK_RESET, 0, R.string.reset_network_title); item = menu.add(0, mMenuId, 0, R.string.reset_network_title); } } if (item != null) { if (item != null) { item.setOnMenuItemClickListener(target -> { item.setOnMenuItemClickListener(target -> { Loading src/com/android/settings/network/PrivateDnsMenuController.java 0 → 100644 +44 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2017 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 android.app.FragmentManager; import android.view.Menu; import android.view.MenuItem; import com.android.settings.R; public class PrivateDnsMenuController { private final FragmentManager mFragmentManager; private final int mMenuId; public PrivateDnsMenuController(FragmentManager fragmentManager, int menuId) { mFragmentManager = fragmentManager; mMenuId = menuId; } public void buildMenuItem(Menu menu) { if (menu != null) { MenuItem item = menu.add(0 /* groupId */, mMenuId, 0 /* order */, R.string.select_private_dns_configuration_title); item.setOnMenuItemClickListener(target -> { PrivateDnsModeDialogFragment.show(mFragmentManager); return true; }); } } } src/com/android/settings/network/PrivateDnsModeDialogFragment.java 0 → 100644 +168 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2017 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.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.app.AlertDialog; import android.app.Dialog; import android.app.FragmentManager; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.RadioGroup; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import java.util.HashMap; import java.util.Map; /** * Dialog to set the private dns */ public class PrivateDnsModeDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener, RadioGroup.OnCheckedChangeListener, TextWatcher { private static final String TAG = "PrivateDnsModeDialogFragment"; // DNS_MODE -> RadioButton id private static final Map<String, Integer> PRIVATE_DNS_MAP; static { PRIVATE_DNS_MAP = new HashMap<>(); PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OFF, R.id.private_dns_mode_off); PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OPPORTUNISTIC, R.id.private_dns_mode_opportunistic); PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, R.id.private_dns_mode_provider); } @VisibleForTesting static final String MODE_KEY = Settings.Global.PRIVATE_DNS_MODE; @VisibleForTesting static final String HOSTNAME_KEY = Settings.Global.PRIVATE_DNS_SPECIFIER; @VisibleForTesting EditText mEditText; @VisibleForTesting RadioGroup mRadioGroup; @VisibleForTesting String mMode; public static void show(FragmentManager fragmentManager) { if (fragmentManager.findFragmentByTag(TAG) == null) { final PrivateDnsModeDialogFragment fragment = new PrivateDnsModeDialogFragment(); fragment.show(fragmentManager, TAG); } } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Context context = getContext(); return new AlertDialog.Builder(context) .setTitle(R.string.select_private_dns_configuration_title) .setView(buildPrivateDnsView(context)) .setPositiveButton(R.string.save, this) .setNegativeButton(R.string.dlg_cancel, null) .create(); } private View buildPrivateDnsView(final Context context) { final ContentResolver contentResolver = context.getContentResolver(); final String mode = Settings.Global.getString(contentResolver, MODE_KEY); final View view = LayoutInflater.from(context).inflate(R.layout.private_dns_mode_dialog, null); mEditText = view.findViewById(R.id.private_dns_mode_provider_hostname); mEditText.addTextChangedListener(this); mEditText.setText(Settings.Global.getString(contentResolver, HOSTNAME_KEY)); mRadioGroup = view.findViewById(R.id.private_dns_radio_group); mRadioGroup.setOnCheckedChangeListener(this); mRadioGroup.check(PRIVATE_DNS_MAP.getOrDefault(mode, R.id.private_dns_mode_opportunistic)); return view; } @Override public void onClick(DialogInterface dialog, int which) { //TODO(b/34953048): add metric action if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { // Only clickable if hostname is valid, so we could save it safely Settings.Global.putString(getContext().getContentResolver(), HOSTNAME_KEY, mEditText.getText().toString()); } Settings.Global.putString(getContext().getContentResolver(), MODE_KEY, mMode); } @Override public int getMetricsCategory() { //TODO(b/68030013): add metric id return 0; } @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId) { case R.id.private_dns_mode_off: mMode = PRIVATE_DNS_MODE_OFF; mEditText.setEnabled(false); break; case R.id.private_dns_mode_opportunistic: mMode = PRIVATE_DNS_MODE_OPPORTUNISTIC; mEditText.setEnabled(false); break; case R.id.private_dns_mode_provider: mMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; mEditText.setEnabled(true); break; } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { // TODO(b/68030013): Disable the "positive button" ("Save") when appearsValid is false. final boolean valid = isWeaklyValidatedHostname(s.toString()); } private boolean isWeaklyValidatedHostname(String hostname) { // TODO(b/34953048): Find and use a better validation method. Specifically: // [1] this should reject IP string literals, and // [2] do the best, simplest, future-proof verification that // the input approximates a DNS hostname. final String WEAK_HOSTNAME_REGEX = "^[a-zA-Z0-9_.-]+$"; return hostname.matches(WEAK_HOSTNAME_REGEX); } } Loading
res/layout/private_dns_mode_dialog.xml +6 −9 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ <RadioGroup <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/private_dns_radio_group" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="wrap_content" android:padding="8dip"> android:padding="8dip"> Loading @@ -25,24 +26,21 @@ android:text="@string/private_dns_mode_off" android:text="@string/private_dns_mode_off" android:layout_width="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip" android:layout_margin="8dip"/> /> <RadioButton <RadioButton android:id="@+id/private_dns_mode_opportunistic" android:id="@+id/private_dns_mode_opportunistic" android:text="@string/private_dns_mode_opportunistic" android:text="@string/private_dns_mode_opportunistic" android:layout_width="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip" android:layout_margin="8dip"/> /> <RadioButton <RadioButton android:id="@+id/private_dns_mode_provider" android:id="@+id/private_dns_mode_provider" android:text="@string/private_dns_mode_provider" android:text="@string/private_dns_mode_provider" android:layout_width="wrap_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_margin="8dip" android:layout_margin="8dip"/> /> <EditText <EditText android:id="@+id/private_dns_mode_provider_hostname" android:id="@+id/private_dns_mode_provider_hostname" Loading @@ -52,8 +50,7 @@ android:inputType="textFilter|textUri" android:inputType="textFilter|textUri" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dip" android:layout_marginStart="40dip" android:layout_marginEnd="8dip" android:layout_marginEnd="8dip"/> /> </RadioGroup> </RadioGroup>
src/com/android/settings/network/NetworkDashboardFragment.java +7 −1 Original line number Original line Diff line number Diff line Loading @@ -48,8 +48,11 @@ public class NetworkDashboardFragment extends DashboardFragment implements MobilePlanPreferenceHost { MobilePlanPreferenceHost { private static final String TAG = "NetworkDashboardFrag"; private static final String TAG = "NetworkDashboardFrag"; private static final int MENU_NETWORK_RESET = Menu.FIRST; private static final int MENU_PRIVATE_DNS = Menu.FIRST + 1; private NetworkResetActionMenuController mNetworkResetController; private NetworkResetActionMenuController mNetworkResetController; private PrivateDnsMenuController mPrivateDnsMenuController; @Override @Override public int getMetricsCategory() { public int getMetricsCategory() { Loading @@ -69,7 +72,9 @@ public class NetworkDashboardFragment extends DashboardFragment implements @Override @Override public void onAttach(Context context) { public void onAttach(Context context) { super.onAttach(context); super.onAttach(context); mNetworkResetController = new NetworkResetActionMenuController(context); mNetworkResetController = new NetworkResetActionMenuController(context, MENU_NETWORK_RESET); mPrivateDnsMenuController = new PrivateDnsMenuController(getFragmentManager(), MENU_PRIVATE_DNS); } } @Override @Override Loading @@ -81,6 +86,7 @@ public class NetworkDashboardFragment extends DashboardFragment implements public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater); mNetworkResetController.buildMenuItem(menu); mNetworkResetController.buildMenuItem(menu); mPrivateDnsMenuController.buildMenuItem(menu); } } @Override @Override Loading
src/com/android/settings/network/NetworkResetActionMenuController.java +4 −3 Original line number Original line Diff line number Diff line Loading @@ -27,19 +27,20 @@ import com.android.settings.Utils; public class NetworkResetActionMenuController { public class NetworkResetActionMenuController { private static final int MENU_NETWORK_RESET = Menu.FIRST + 200; private final Context mContext; private final Context mContext; private final NetworkResetRestrictionChecker mRestrictionChecker; private final NetworkResetRestrictionChecker mRestrictionChecker; private final int mMenuId; public NetworkResetActionMenuController(Context context) { public NetworkResetActionMenuController(Context context, int menuId) { mContext = context; mContext = context; mRestrictionChecker = new NetworkResetRestrictionChecker(context); mRestrictionChecker = new NetworkResetRestrictionChecker(context); mMenuId = menuId; } } public void buildMenuItem(Menu menu) { public void buildMenuItem(Menu menu) { MenuItem item = null; MenuItem item = null; if (isAvailable() && menu != null) { if (isAvailable() && menu != null) { item = menu.add(0, MENU_NETWORK_RESET, 0, R.string.reset_network_title); item = menu.add(0, mMenuId, 0, R.string.reset_network_title); } } if (item != null) { if (item != null) { item.setOnMenuItemClickListener(target -> { item.setOnMenuItemClickListener(target -> { Loading
src/com/android/settings/network/PrivateDnsMenuController.java 0 → 100644 +44 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2017 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 android.app.FragmentManager; import android.view.Menu; import android.view.MenuItem; import com.android.settings.R; public class PrivateDnsMenuController { private final FragmentManager mFragmentManager; private final int mMenuId; public PrivateDnsMenuController(FragmentManager fragmentManager, int menuId) { mFragmentManager = fragmentManager; mMenuId = menuId; } public void buildMenuItem(Menu menu) { if (menu != null) { MenuItem item = menu.add(0 /* groupId */, mMenuId, 0 /* order */, R.string.select_private_dns_configuration_title); item.setOnMenuItemClickListener(target -> { PrivateDnsModeDialogFragment.show(mFragmentManager); return true; }); } } }
src/com/android/settings/network/PrivateDnsModeDialogFragment.java 0 → 100644 +168 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2017 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.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.app.AlertDialog; import android.app.Dialog; import android.app.FragmentManager; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.RadioGroup; import com.android.settings.R; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import java.util.HashMap; import java.util.Map; /** * Dialog to set the private dns */ public class PrivateDnsModeDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener, RadioGroup.OnCheckedChangeListener, TextWatcher { private static final String TAG = "PrivateDnsModeDialogFragment"; // DNS_MODE -> RadioButton id private static final Map<String, Integer> PRIVATE_DNS_MAP; static { PRIVATE_DNS_MAP = new HashMap<>(); PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OFF, R.id.private_dns_mode_off); PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_OPPORTUNISTIC, R.id.private_dns_mode_opportunistic); PRIVATE_DNS_MAP.put(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, R.id.private_dns_mode_provider); } @VisibleForTesting static final String MODE_KEY = Settings.Global.PRIVATE_DNS_MODE; @VisibleForTesting static final String HOSTNAME_KEY = Settings.Global.PRIVATE_DNS_SPECIFIER; @VisibleForTesting EditText mEditText; @VisibleForTesting RadioGroup mRadioGroup; @VisibleForTesting String mMode; public static void show(FragmentManager fragmentManager) { if (fragmentManager.findFragmentByTag(TAG) == null) { final PrivateDnsModeDialogFragment fragment = new PrivateDnsModeDialogFragment(); fragment.show(fragmentManager, TAG); } } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Context context = getContext(); return new AlertDialog.Builder(context) .setTitle(R.string.select_private_dns_configuration_title) .setView(buildPrivateDnsView(context)) .setPositiveButton(R.string.save, this) .setNegativeButton(R.string.dlg_cancel, null) .create(); } private View buildPrivateDnsView(final Context context) { final ContentResolver contentResolver = context.getContentResolver(); final String mode = Settings.Global.getString(contentResolver, MODE_KEY); final View view = LayoutInflater.from(context).inflate(R.layout.private_dns_mode_dialog, null); mEditText = view.findViewById(R.id.private_dns_mode_provider_hostname); mEditText.addTextChangedListener(this); mEditText.setText(Settings.Global.getString(contentResolver, HOSTNAME_KEY)); mRadioGroup = view.findViewById(R.id.private_dns_radio_group); mRadioGroup.setOnCheckedChangeListener(this); mRadioGroup.check(PRIVATE_DNS_MAP.getOrDefault(mode, R.id.private_dns_mode_opportunistic)); return view; } @Override public void onClick(DialogInterface dialog, int which) { //TODO(b/34953048): add metric action if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { // Only clickable if hostname is valid, so we could save it safely Settings.Global.putString(getContext().getContentResolver(), HOSTNAME_KEY, mEditText.getText().toString()); } Settings.Global.putString(getContext().getContentResolver(), MODE_KEY, mMode); } @Override public int getMetricsCategory() { //TODO(b/68030013): add metric id return 0; } @Override public void onCheckedChanged(RadioGroup group, int checkedId) { switch (checkedId) { case R.id.private_dns_mode_off: mMode = PRIVATE_DNS_MODE_OFF; mEditText.setEnabled(false); break; case R.id.private_dns_mode_opportunistic: mMode = PRIVATE_DNS_MODE_OPPORTUNISTIC; mEditText.setEnabled(false); break; case R.id.private_dns_mode_provider: mMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; mEditText.setEnabled(true); break; } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { // TODO(b/68030013): Disable the "positive button" ("Save") when appearsValid is false. final boolean valid = isWeaklyValidatedHostname(s.toString()); } private boolean isWeaklyValidatedHostname(String hostname) { // TODO(b/34953048): Find and use a better validation method. Specifically: // [1] this should reject IP string literals, and // [2] do the best, simplest, future-proof verification that // the input approximates a DNS hostname. final String WEAK_HOSTNAME_REGEX = "^[a-zA-Z0-9_.-]+$"; return hostname.matches(WEAK_HOSTNAME_REGEX); } }