From b2252cf1a4b22d60dae76300415ad5bbc6cd0a89 Mon Sep 17 00:00:00 2001
From: Martin Brabham <optedoblivion@cyngn.com>
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 @@
             </intent-filter>
         </receiver>
 
+        <activity
+            android:name=".dnscrypt.DnsCryptPreferenceActivity"
+            android:label="DNS Encryption"
+            android:excludeFromRecents="true">
+            <intent-filter>
+                <action android:name="com.android.settings.action.ACTION_DNSCRYPT_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".applications.ProtectedAppsActivity"
                   android:label="@string/protected_apps"
                   android:excludeFromRecents="true"
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index 6b42f4e..51aff3c 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -1163,4 +1163,15 @@
     <string name="restrict_app_cellular_title">Disable app cellular usage</string>
     <string name="restrict_app_cellular_summary">Prevent app from using cellular data connection</string>
     <string name="privacy_guard_internet_category">Internet</string>
+
+    <!-- DNS Encryption -->
+    <string name="dns_enc_title">DNS encryption</string>
+    <string name="dns_enc_summary">Manage DNS encryption settings</string>
+    <string name="dns_enc_switch_title">Enable DNS encryption</string>
+    <string name="dns_enc_switch_summary">Toggle execution of local DNS encryption service</string>
+    <string name="dns_enc_cat_config_title">Configuration</string>
+    <string name="dns_enc_server_title">DNS resolver list</string>
+    <string name="dns_enc_server_summary">Select which server you want to use</string>
+    <string name="dns_enc_resolver_label">DNS resolver:&#160;</string>
+    <string name="dns_enc_resolvers_loading">Loading encrypted DNS resolver configuration...</string>
 </resources>
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+    android:title="@string/radio_controls_title">
+
+    <SwitchPreference
+        android:key="pref_toggle_dnscrypt"
+        android:title="@string/dns_enc_switch_title"
+        android:summary="@string/dns_enc_switch_summary"
+        android:persistent="false"/>
+
+    <PreferenceCategory
+        android:key="pref_cat_config"
+        android:title="@string/dns_enc_cat_config_title">
+        <ListPreference
+            android:key="pref_server_list"
+            android:title="@string/dns_enc_server_title"
+            android:summary="@string/dns_enc_server_summary"
+            android:persistent="false"/>
+    </PreferenceCategory>
+
+</PreferenceScreen>
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" />
         </Preference>
+
+        <PreferenceScreen
+            android:key="key_dns_crypt"
+            android:title="@string/dns_enc_title"
+            android:summary="@string/dns_enc_summary">
+            <intent
+                android:action="com.android.settings.action.ACTION_DNSCRYPT_SETTINGS"
+                android:targetPackage="com.android.settings"
+                android:targetClass="com.android.settings.dnscrypt.DnsCryptPreferenceActivity" />
+        </PreferenceScreen>
 </PreferenceScreen>
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
+ * <pre>
+ *     Screen for configuring and starting stopping encrypted dns daemon
+ * </pre>
+ *
+ * @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
+ * <pre>
+ *     This task will check if the config file exists in /system/etc and if so will load it for
+ *     configuration display in Settings
+ * </pre>
+ */
+public class LoadConfigFileTask extends AsyncTask<Void, Integer, Void> {
+
+    // 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<String> configLines = new ArrayList<String>();
+            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
+     * <pre>
+     *     Callback interface for passing back results
+     * </pre>
+     */
+    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
+ * <pre>
+ *    Model object for representing a dns crypt server entry
+ * </pre>
+ */
+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

