Skip to content

Commit fb58cd9

Browse files
committed
Control Private DNS mode
Add a new dialog to control the Private DNS mode on the device using the new DevicePolicyManager API. The new dialog (under "Set Private DNS Mode") loads the current mode and host values using the get methods in the DPM, and will set the host & mode when the Set button is clicked. Test: Manual Bug: 112982691 Change-Id: I1f5c236ee409929ec76471c52e1366ae0169946f
1 parent a13fd41 commit fb58cd9

File tree

5 files changed

+274
-0
lines changed

5 files changed

+274
-0
lines changed

app/src/main/java/com/afwsamples/testdpc/policy/PolicyManagementFragment.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
import com.afwsamples.testdpc.policy.locktask.SetLockTaskFeaturesFragment;
109109
import com.afwsamples.testdpc.policy.networking.AlwaysOnVpnFragment;
110110
import com.afwsamples.testdpc.policy.networking.NetworkUsageStatsFragment;
111+
import com.afwsamples.testdpc.policy.networking.PrivateDnsModeFragment;
111112
import com.afwsamples.testdpc.policy.resetpassword.ResetPasswordWithTokenFragment;
112113
import com.afwsamples.testdpc.policy.systemupdatepolicy.SystemUpdatePolicyFragment;
113114
import com.afwsamples.testdpc.policy.wifimanagement.WifiConfigCreationDialog;
@@ -365,6 +366,8 @@ public class PolicyManagementFragment extends BaseSearchablePolicyPreferenceFrag
365366

366367
private static final String MANAGE_OVERRIDE_APN_KEY = "manage_override_apn";
367368

369+
private static final String SET_PRIVATE_DNS_MODE_KEY = "set_private_dns_mode";
370+
368371
private static final String BATTERY_PLUGGED_ANY = Integer.toString(
369372
BatteryManager.BATTERY_PLUGGED_AC |
370373
BatteryManager.BATTERY_PLUGGED_USB |
@@ -492,6 +495,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
492495
findPreference(SET_ALWAYS_ON_VPN_KEY).setOnPreferenceClickListener(this);
493496
findPreference(SET_GLOBAL_HTTP_PROXY_KEY).setOnPreferenceClickListener(this);
494497
findPreference(CLEAR_GLOBAL_HTTP_PROXY_KEY).setOnPreferenceClickListener(this);
498+
findPreference(SET_PRIVATE_DNS_MODE_KEY).setOnPreferenceClickListener(this);
495499
findPreference(NETWORK_STATS_KEY).setOnPreferenceClickListener(this);
496500
findPreference(DELEGATED_CERT_INSTALLER_KEY).setOnPreferenceClickListener(this);
497501
mDisableStatusBarPreference = (DpcPreference) findPreference(DISABLE_STATUS_BAR);
@@ -929,6 +933,9 @@ public void onPositiveButtonClicked(String[] lockTaskArray) {
929933
mDevicePolicyManager.setRecommendedGlobalProxy(mAdminComponentName,
930934
null /* proxyInfo */);
931935
return true;
936+
case SET_PRIVATE_DNS_MODE_KEY:
937+
showFragment(new PrivateDnsModeFragment());
938+
return true;
932939
case NETWORK_STATS_KEY:
933940
showFragment(new NetworkUsageStatsFragment());
934941
return true;
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
* Copyright (C) 2018 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.afwsamples.testdpc.policy.networking;
18+
19+
import android.annotation.TargetApi;
20+
import android.app.Fragment;
21+
import android.app.admin.DevicePolicyManager;
22+
import android.content.ComponentName;
23+
import android.content.Context;
24+
import android.os.Bundle;
25+
import android.util.Log;
26+
import android.view.LayoutInflater;
27+
import android.view.View;
28+
import android.view.ViewGroup;
29+
import android.widget.Button;
30+
import android.widget.EditText;
31+
import android.widget.RadioGroup;
32+
import android.widget.Toast;
33+
34+
import com.afwsamples.testdpc.DeviceAdminReceiver;
35+
import com.afwsamples.testdpc.R;
36+
import com.afwsamples.testdpc.common.ReflectionUtil;
37+
38+
@TargetApi(29)
39+
public class PrivateDnsModeFragment extends Fragment implements View.OnClickListener,
40+
RadioGroup.OnCheckedChangeListener {
41+
private static final String TAG = "PDNS_FRAG";
42+
43+
// Copied from DevicePolicyManager, should be removed when the code is migrated to Q.
44+
static final int PRIVATE_DNS_MODE_UNKNOWN = 0;
45+
static final int PRIVATE_DNS_MODE_OFF = 1;
46+
static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2;
47+
static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
48+
49+
private DevicePolicyManager mDpm;
50+
private RadioGroup mPrivateDnsModeSelection;
51+
private Button mSetButton;
52+
private EditText mCurrentResolver;
53+
// The mode that is currently selected in the radio group.
54+
private int mSelectedMode;
55+
56+
@Override
57+
public void onCreate(Bundle savedInstanceState) {
58+
super.onCreate(savedInstanceState);
59+
mDpm = (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
60+
mSelectedMode = PRIVATE_DNS_MODE_UNKNOWN;
61+
}
62+
63+
@Override
64+
public void onClick(View view) {
65+
String resolver = mCurrentResolver.getText().toString();
66+
setPrivateDnsMode(mSelectedMode, resolver);
67+
}
68+
69+
@Override
70+
public void onCheckedChanged(RadioGroup group, int checkedId) {
71+
updateSelectedMode(checkedId);
72+
}
73+
74+
@Override
75+
public View onCreateView(LayoutInflater inflater, ViewGroup container,
76+
Bundle savedInstanceState) {
77+
View view = inflater.inflate(R.layout.private_dns_mode, null);
78+
mSetButton = view.findViewById(R.id.private_dns_mode_apply);
79+
mSetButton.setOnClickListener(this);
80+
81+
mPrivateDnsModeSelection = view.findViewById(R.id.private_dns_mode_selection);
82+
int currentMode = getPrivateDnsMode();
83+
switch (currentMode) {
84+
case PRIVATE_DNS_MODE_OFF:
85+
mPrivateDnsModeSelection.check(R.id.private_dns_mode_off);
86+
break;
87+
case PRIVATE_DNS_MODE_OPPORTUNISTIC:
88+
mPrivateDnsModeSelection.check(R.id.private_dns_mode_automatic);
89+
break;
90+
case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
91+
mPrivateDnsModeSelection.check(R.id.private_dns_mode_specific_host);
92+
break;
93+
default:
94+
mPrivateDnsModeSelection.check(R.id.private_dns_mode_unknown);
95+
break;
96+
}
97+
mPrivateDnsModeSelection.setOnCheckedChangeListener(this);
98+
updateSelectedMode(mPrivateDnsModeSelection.getCheckedRadioButtonId());
99+
100+
mCurrentResolver = view.findViewById(R.id.private_dns_resolver);
101+
mCurrentResolver.setText(getPrivateDnsHost());
102+
return view;
103+
}
104+
105+
private void updateSelectedMode(int checkedId) {
106+
switch (checkedId) {
107+
case R.id.private_dns_mode_off:
108+
mSelectedMode = PRIVATE_DNS_MODE_OFF;
109+
break;
110+
case R.id.private_dns_mode_automatic:
111+
mSelectedMode = PRIVATE_DNS_MODE_OPPORTUNISTIC;
112+
break;
113+
case R.id.private_dns_mode_specific_host:
114+
mSelectedMode = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
115+
break;
116+
case R.id.private_dns_mode_unknown:
117+
default:
118+
mSelectedMode = PRIVATE_DNS_MODE_UNKNOWN;
119+
break;
120+
}
121+
}
122+
123+
private int getPrivateDnsMode() {
124+
try {
125+
return (Integer) ReflectionUtil.invoke(
126+
mDpm, "getGlobalPrivateDnsMode",
127+
DeviceAdminReceiver.getComponentName(getActivity()));
128+
} catch (ReflectionUtil.ReflectionIsTemporaryException e) {
129+
Log.w(TAG, "Failure getting current mode", e);
130+
}
131+
132+
return PRIVATE_DNS_MODE_UNKNOWN;
133+
}
134+
135+
private String getPrivateDnsHost() {
136+
try {
137+
return (String) ReflectionUtil.invoke(
138+
mDpm, "getGlobalPrivateDnsHost",
139+
DeviceAdminReceiver.getComponentName(getActivity()));
140+
} catch (ReflectionUtil.ReflectionIsTemporaryException e) {
141+
Log.w(TAG, "Failure getting host", e);
142+
}
143+
144+
return "<error getting resolver>";
145+
}
146+
147+
private void setPrivateDnsMode(int mode, String resolver) {
148+
Log.w(TAG, String.format("Setting mode %d host %s", mSelectedMode, resolver));
149+
try {
150+
ReflectionUtil.invoke(
151+
mDpm, "setGlobalPrivateDns",
152+
new Class[]{ComponentName.class, int.class, String.class},
153+
DeviceAdminReceiver.getComponentName(getActivity()),
154+
mode,
155+
resolver);
156+
Toast.makeText(getActivity(), R.string.setting_private_dns_succeess,
157+
Toast.LENGTH_LONG).show();
158+
} catch (ReflectionUtil.ReflectionIsTemporaryException e) {
159+
Log.w(TAG, "Failed to invoke, cause", e);
160+
161+
// This is the real exception.
162+
Throwable causeOfCause = e.getCause().getCause();
163+
164+
Toast.makeText(getActivity(),
165+
"Failure: " + causeOfCause.getMessage(), Toast.LENGTH_LONG).show();
166+
}
167+
}
168+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
Copyright (C) 2018 The Android Open Source Project
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
18+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
19+
android:layout_width="match_parent"
20+
android:layout_height="match_parent">
21+
22+
<TextView
23+
android:id="@+id/private_dns_mode_label"
24+
android:layout_width="wrap_content"
25+
android:layout_height="32dp"
26+
android:text="@string/current_private_dns_mode"/>
27+
28+
<RadioGroup
29+
android:id="@+id/private_dns_mode_selection"
30+
android:layout_width="fill_parent"
31+
android:layout_height="wrap_content"
32+
android:layout_below="@+id/private_dns_mode_label"
33+
android:layout_alignParentLeft="true"
34+
android:layout_marginTop="32dp"
35+
android:orientation="vertical">
36+
37+
<RadioButton android:id="@+id/private_dns_mode_off"
38+
android:layout_width="wrap_content"
39+
android:layout_height="wrap_content"
40+
android:text="@string/private_dns_mode_off"/>
41+
42+
<RadioButton android:id="@+id/private_dns_mode_automatic"
43+
android:layout_width="wrap_content"
44+
android:layout_height="wrap_content"
45+
android:text="@string/private_dns_mode_automatic"/>
46+
47+
<RadioButton android:id="@+id/private_dns_mode_specific_host"
48+
android:layout_width="wrap_content"
49+
android:layout_height="wrap_content"
50+
android:text="@string/private_dns_mode_specific_host"/>
51+
52+
<RadioButton android:id="@+id/private_dns_mode_unknown"
53+
android:layout_width="wrap_content"
54+
android:layout_height="wrap_content"
55+
android:text="@string/private_dns_mode_unknown"/>
56+
57+
</RadioGroup>
58+
59+
<LinearLayout
60+
android:id="@+id/private_dns_resolver_text"
61+
android:layout_width="match_parent"
62+
android:layout_height="wrap_content"
63+
android:layout_below="@+id/private_dns_mode_selection"
64+
style="@style/networking_item">
65+
<TextView
66+
android:layout_width="wrap_content"
67+
android:layout_height="wrap_content"
68+
android:text="@string/networking_proxy_host"
69+
style="@style/networking_item_label"/>
70+
<EditText android:id="@+id/private_dns_resolver"
71+
android:layout_width="match_parent"
72+
android:layout_height="wrap_content"
73+
android:hint="@string/networking_proxy_host_hint"
74+
android:singleLine="true"
75+
style="@style/networking_item_edit_content"/>
76+
</LinearLayout>
77+
78+
<Button android:id="@+id/private_dns_mode_apply"
79+
android:layout_width="fill_parent"
80+
android:layout_height="wrap_content"
81+
android:layout_alignParentLeft="true"
82+
android:layout_marginTop="48dip"
83+
android:layout_below="@+id/private_dns_resolver_text"
84+
android:text="@string/private_dns_mode_apply"/>
85+
86+
</RelativeLayout>

app/src/main/res/values/strings.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,14 @@
754754
<string name="no_host">No host specified.</string>
755755
<string name="no_port">No port specified.</string>
756756
<string name="port_out_of_range">Port out of range.</string>
757+
<string name="set_global_private_dns">Set Private DNS Mode</string>
758+
<string name="current_private_dns_mode">Current Mode</string>
759+
<string name="private_dns_mode_off">Off</string>
760+
<string name="private_dns_mode_automatic">Automatic</string>
761+
<string name="private_dns_mode_specific_host">Specified Host</string>
762+
<string name="private_dns_mode_unknown">Unknown</string>
763+
<string name="private_dns_mode_apply">Set</string>
764+
<string name="setting_private_dns_succeess">Success setting Private DNS</string>
757765

758766
<!-- Strings for data usage -->
759767
<string name="data_usage">Data usage</string>

app/src/main/res/xml/device_policy_header.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,11 @@
395395
android:title="@string/clear_global_http_proxy"
396396
testdpc:admin="deviceOwner"
397397
testdpc:minSdkVersion="L" />
398+
<com.afwsamples.testdpc.common.preference.DpcPreference
399+
android:key="set_private_dns_mode"
400+
android:title="@string/set_global_private_dns"
401+
testdpc:admin="deviceOwner"
402+
testdpc:minSdkVersion="Q" />
398403
</PreferenceCategory>
399404

400405
<PreferenceCategory android:title="@string/permission_management">

0 commit comments

Comments
 (0)