From b2252cf1a4b22d60dae76300415ad5bbc6cd0a89 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Fri, 9 Jan 2015 14:49:10 -0800 Subject: [PATCH] DNSCrypt: Add settings for starting/stopping and configuring the DNSCrypt daemon. Wire up to framework Settings.Secure Change-Id: I846e30f7121460b1eed9879d9d225ffe1506050b --- AndroidManifest.xml | 10 ++ res/values/cm_strings.xml | 11 ++ res/xml/dns_crypt_settings.xml | 38 +++++ res/xml/privacy_settings_cyanogenmod.xml | 10 ++ .../dnscrypt/DnsCryptPreferenceActivity.java | 149 ++++++++++++++++++++ .../settings/dnscrypt/LoadConfigFileTask.java | 152 ++++++++++++++++++++ .../dnscrypt/model/DnsCryptServerEntry.java | 155 +++++++++++++++++++++ 7 files changed, 525 insertions(+) create mode 100644 res/xml/dns_crypt_settings.xml create mode 100644 src/com/android/settings/dnscrypt/DnsCryptPreferenceActivity.java create mode 100644 src/com/android/settings/dnscrypt/LoadConfigFileTask.java create mode 100644 src/com/android/settings/dnscrypt/model/DnsCryptServerEntry.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f38e0bc..ed95f32 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2868,6 +2868,16 @@ + + + + + + + Disable app cellular usage Prevent app from using cellular data connection Internet + + + DNS encryption + Manage DNS encryption settings + Enable DNS encryption + Toggle execution of local DNS encryption service + Configuration + DNS resolver list + Select which server you want to use + DNS resolver:  + Loading encrypted DNS resolver configuration... diff --git a/res/xml/dns_crypt_settings.xml b/res/xml/dns_crypt_settings.xml new file mode 100644 index 0000000..636f754 --- /dev/null +++ b/res/xml/dns_crypt_settings.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/res/xml/privacy_settings_cyanogenmod.xml b/res/xml/privacy_settings_cyanogenmod.xml index 256ca00..25221c2 100644 --- a/res/xml/privacy_settings_cyanogenmod.xml +++ b/res/xml/privacy_settings_cyanogenmod.xml @@ -47,4 +47,14 @@ android:targetPackage="com.android.settings" android:targetClass="com.android.settings.applications.ProtectedAppsActivity" /> + + + + diff --git a/src/com/android/settings/dnscrypt/DnsCryptPreferenceActivity.java b/src/com/android/settings/dnscrypt/DnsCryptPreferenceActivity.java new file mode 100644 index 0000000..64f13c7 --- /dev/null +++ b/src/com/android/settings/dnscrypt/DnsCryptPreferenceActivity.java @@ -0,0 +1,149 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.dnscrypt; + +import android.app.ProgressDialog; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceActivity; +import android.preference.SwitchPreference; +import android.provider.Settings; +import android.util.Log; +import com.android.settings.R; +import com.android.settings.dnscrypt.LoadConfigFileTask.ILoadConfigListener; + +import java.io.File; + +/** + * DnsCryptSettingsFragment + *
+ *     Screen for configuring and starting stopping encrypted dns daemon
+ * 
+ * + * @see {@link android.preference.PreferenceActivity} + * @see {@link android.preference.Preference.OnPreferenceChangeListener} + */ +public class DnsCryptPreferenceActivity extends PreferenceActivity implements + OnPreferenceChangeListener, ILoadConfigListener { + + // Constants + private static final String TAG = DnsCryptPreferenceActivity.class.getSimpleName(); + private static final String PREF_TOGGLE = "pref_toggle_dnscrypt"; + private static final String PREF_SERVER_LIST = "pref_server_list"; + private static final String CONFIG_FILE = "/system/etc/dnscrypt-resolvers.csv"; + + // Members + private SwitchPreference mEncryptionSwitchPreference; + private ListPreference mServerListPreference; + private LoadConfigFileTask mLoadConfigFileTask; + private String mDnsResolverLabel = "DNS provider"; + + // Views + private ProgressDialog mProgressDialog; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getActionBar().setDisplayHomeAsUpEnabled(true); + getActionBar().setTitle(R.string.dns_enc_title); + addPreferencesFromResource(R.xml.dns_crypt_settings); + mEncryptionSwitchPreference = (SwitchPreference) findPreference(PREF_TOGGLE); + mEncryptionSwitchPreference.setOnPreferenceChangeListener(this); + mServerListPreference = (ListPreference) findPreference(PREF_SERVER_LIST); + mServerListPreference.setOnPreferenceChangeListener(this); + mDnsResolverLabel = getResources().getString(R.string.dns_enc_resolver_label); + mProgressDialog = new ProgressDialog(this); + mProgressDialog.setTitle(R.string.dns_enc_resolvers_loading); + mProgressDialog.show(); + if (mLoadConfigFileTask == null) { + mLoadConfigFileTask = + new LoadConfigFileTask(new File(CONFIG_FILE), mProgressDialog, this); + mLoadConfigFileTask.execute(); + } + initPreferences(); + } + + private void initPreferences() { + boolean currentCheckedState = (Settings.Secure.getInt(getContentResolver(), + Settings.Secure.DNS_ENCRYPTION_TOGGLE, + Settings.Secure.DNS_ENCRYPTION_TOGGLE_DEFAULT) == 1); + if (mEncryptionSwitchPreference != null) { + mEncryptionSwitchPreference.setChecked(currentCheckedState); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object o) { + if (preference == mEncryptionSwitchPreference) { + Boolean newCheckedState = (Boolean) o; + mServerListPreference.setEnabled(newCheckedState); + int setValue = (newCheckedState) ? 1 : 0; + Settings.Secure.putInt(getContentResolver(), Settings.Secure.DNS_ENCRYPTION_TOGGLE, + setValue); + return true; + } else if (preference == mServerListPreference) { + String value = (String) o; + int i; + for (i = 0; i < mServerListPreference.getEntryValues().length; i++) { + if (value.equals(mServerListPreference.getEntryValues()[i])) { + break; + } + } + try { + mServerListPreference.setSummary(mDnsResolverLabel + + mServerListPreference.getEntries()[i]); + } catch (ArrayIndexOutOfBoundsException oobe) { + Log.e(TAG, "Error finding default value!"); + mServerListPreference.setSummary(R.string.dns_enc_server_summary); + } + Settings.Secure.putString(getContentResolver(), Settings.Secure + .DNS_ENCRYPTION_SERVER, value); + return true; + } + return false; + } + + @Override + public void onConfigLoaded(CharSequence[] entries, CharSequence[] entryValues) { + if (mServerListPreference != null && entries != null && entryValues != null) { + mServerListPreference.setEntries(entries); + mServerListPreference.setEntryValues(entryValues); + String value = Settings.Secure.getString(getContentResolver(), + Settings.Secure.DNS_ENCRYPTION_SERVER); + value = (value == null) ? Settings.Secure.DNS_ENCRYPTION_SERVER_DEFAULT : value; + mServerListPreference.setValue(value); + int i; + for (i = 0; i < mServerListPreference.getEntryValues().length; i++) { + if (value.equals(mServerListPreference.getEntryValues()[i])) { + break; + } + } + try { + mServerListPreference.setSummary(mDnsResolverLabel + + mServerListPreference.getEntries()[i]); + } catch (ArrayIndexOutOfBoundsException oobe) { + Log.e(TAG, "Error finding default value!", oobe); + mServerListPreference.setSummary(R.string.dns_enc_server_summary); + } + mServerListPreference.setEnabled(mEncryptionSwitchPreference.isChecked()); + } + mLoadConfigFileTask = null; + mProgressDialog.dismiss(); + } +} diff --git a/src/com/android/settings/dnscrypt/LoadConfigFileTask.java b/src/com/android/settings/dnscrypt/LoadConfigFileTask.java new file mode 100644 index 0000000..910e6c1 --- /dev/null +++ b/src/com/android/settings/dnscrypt/LoadConfigFileTask.java @@ -0,0 +1,152 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.dnscrypt; + +import android.app.ProgressDialog; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +/** + * LoadConfigFileTask + *
+ *     This task will check if the config file exists in /system/etc and if so will load it for
+ *     configuration display in Settings
+ * 
+ */ +public class LoadConfigFileTask extends AsyncTask { + + // Constants + private static final String TAG = LoadConfigFileTask.class.getSimpleName(); + + // Members + private ILoadConfigListener mListener; + private File mConfigFile; + private ProgressDialog mProgressDialog; + private CharSequence[] mEntries; + private CharSequence[] mEntryValues; + + /** + * Constructor + * + * @param configFile {@link java.io.File} + * @param progressDialog {@link android.app.ProgressDialog} + * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} + */ + public LoadConfigFileTask(File configFile, ProgressDialog progressDialog, ILoadConfigListener + listener) + throws IllegalArgumentException { + if (configFile == null || !configFile.exists()) { + throw new IllegalArgumentException("'configFile' must not be null and must exist!"); + } + mConfigFile = configFile; + mProgressDialog = progressDialog; + mListener = listener; + } + + @Override + public void onPreExecute() { + if (mProgressDialog != null) { + mProgressDialog.show(); + } + } + + @Override + public void onProgressUpdate(Integer... progress) { + if (mProgressDialog != null) { + mProgressDialog.setProgress(progress[0]); + } + } + + @Override + public Void doInBackground(Void... params) { + + if (mConfigFile == null || !mConfigFile.exists()) { + Log.w(TAG, "No config file found....such strange!"); + // [TODO][MSB]: Download that sucker! + // [TODO][MSB]: Perform sig check? + return null; + } + + try { + FileInputStream fis = new FileInputStream(mConfigFile); + InputStreamReader reader = new InputStreamReader(fis); + BufferedReader bufferedReader = new BufferedReader(reader); + List configLines = new ArrayList(); + String line = ""; + while((line = bufferedReader.readLine()) != null) { + configLines.add(line); + } + mEntries = new CharSequence[configLines.size() - 1]; + mEntryValues = new CharSequence[configLines.size() - 1]; + if (mProgressDialog != null) { + mProgressDialog.setMax(configLines.size()); + } + boolean skippedTitle = false; + int progress = 0; + int i = 0; + for (String configLine : configLines) { + if (!skippedTitle) { + skippedTitle = true; + progress++; + continue; + } + String[] configParts = configLine.split(","); + if (configParts.length > 3) { + String name = configParts[0]; + String fullName = configParts[1]; + mEntries[i] = fullName; + mEntryValues[i] = name; + } + i++; + progress++; + publishProgress(progress); + } + } catch (IOException e) { + Log.e(TAG, e.getMessage()); + } + return null; + } + + @Override + public void onPostExecute(Void result) { + if (mListener != null) { + mListener.onConfigLoaded(mEntries, mEntryValues); + } + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + } + } + + /** + * ILoadConfigListener + *
+     *     Callback interface for passing back results
+     * 
+ */ + public interface ILoadConfigListener { + public void onConfigLoaded(CharSequence[] entries, CharSequence[] entryValues); + } + +} diff --git a/src/com/android/settings/dnscrypt/model/DnsCryptServerEntry.java b/src/com/android/settings/dnscrypt/model/DnsCryptServerEntry.java new file mode 100644 index 0000000..c0f2c75 --- /dev/null +++ b/src/com/android/settings/dnscrypt/model/DnsCryptServerEntry.java @@ -0,0 +1,155 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.dnscrypt.model; + +/** + * DnsCryptServerEntry + *
+ *    Model object for representing a dns crypt server entry
+ * 
+ */ +public class DnsCryptServerEntry { + + // Members + private String mName; + private String mFullName; + private String mDescription; + private String mLocation; + private String mCoords; + private String mUrl; + private String mVersion; + private String mDnsSecValidation; + private String mNoLogs; + private String mNamecoin; + private String mResolverAddress; + private String mProviderName; + private String mProviderPublicKey; + private String mProviderPublicKeyTextRecord; + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public String getFullName() { + return mFullName; + } + + public void setFullName(String fullName) { + mFullName = fullName; + } + + public String getDescription() { + return mDescription; + } + + public void setDescription(String description) { + mDescription = description; + } + + public String getLocation() { + return mLocation; + } + + public void setLocation(String location) { + mLocation = location; + } + + public String getCoords() { + return mCoords; + } + + public void setCoords(String coords) { + mCoords = coords; + } + + public String getUrl() { + return mUrl; + } + + public void setUrl(String url) { + mUrl = url; + } + + public String getVersion() { + return mVersion; + } + + public void setVersion(String version) { + mVersion = version; + } + + public String getDnsSecValidation() { + return mDnsSecValidation; + } + + public void setDnsSecValidation(String dnsSecValidation) { + mDnsSecValidation = dnsSecValidation; + } + + public String getNoLogs() { + return mNoLogs; + } + + public void setNoLogs(String noLogs) { + mNoLogs = noLogs; + } + + public String getNamecoin() { + return mNamecoin; + } + + public void setNamecoin(String namecoin) { + mNamecoin = namecoin; + } + + public String getResolverAddress() { + return mResolverAddress; + } + + public void setResolverAddress(String resolverAddress) { + mResolverAddress = resolverAddress; + } + + public String getProviderName() { + return mProviderName; + } + + public void setProviderName(String providerName) { + mProviderName = providerName; + } + + public String getProviderPublicKey() { + return mProviderPublicKey; + } + + public void setProviderPublicKey(String providerPublicKey) { + mProviderPublicKey = providerPublicKey; + } + + public String getProviderPublicKeyTextRecord() { + return mProviderPublicKeyTextRecord; + } + + public void setProviderPublicKeyTextRecord(String providerPublicKeyTextRecord) { + mProviderPublicKeyTextRecord = providerPublicKeyTextRecord; + } + +} -- 2.7.4