service: Logging of console output to UI

Change-Id: I89fb0c0672581c1723a2216e63d7fd2be0e2f6e1
Refs: #2434
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9fad6dd..b562532 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -21,8 +21,12 @@
         </activity>
 
         <activity
-            android:name=".NfdSettingsActivity"
-            android:label="@string/app_name" >
+            android:name=".NfdLogActivity"
+            android:label="@string/nfd_logger" />
+
+        <activity
+            android:name=".NfdLogSettingsActivity"
+            android:label="@string/nfd_log_settings" >
         </activity>
 
         <service
diff --git a/app/src/main/java/net/named_data/nfd/G.java b/app/src/main/java/net/named_data/nfd/G.java
index cacbecc..7310854 100644
--- a/app/src/main/java/net/named_data/nfd/G.java
+++ b/app/src/main/java/net/named_data/nfd/G.java
@@ -38,7 +38,7 @@
   private static final String TAG = "NFDService";
 
   /**
-   * Designated log message method that provides flexibility in message logging.
+   * @brief Designated log message method that provides flexibility in message logging.
    *
    * @param tag Tag to identify log message.
    * @param format Format qualifiers as used in String.format()
@@ -51,7 +51,7 @@
   }
 
   /**
-   * Convenience method to log a message with a specified tag.
+   * @brief Convenience method to log a message with a specified tag.
    *
    * @param tag Tag to identify log message.
    * @param message Output log message.
@@ -61,7 +61,7 @@
   }
 
   /**
-   * Convenience method to log messages with the default tag.
+   * @brief Convenience method to log messages with the default tag.
    *
    * @param message Output log message.
    */
@@ -69,4 +69,11 @@
     Log(TAG, message);
   }
 
+  /**
+   * @brief Gets the tag in which logs are posted with.
+   * @return TAG that is used by this log class.
+   */
+  public static String getLogTag() {
+    return TAG;
+  }
 }
diff --git a/app/src/main/java/net/named_data/nfd/NfdLogActivity.java b/app/src/main/java/net/named_data/nfd/NfdLogActivity.java
new file mode 100644
index 0000000..e7552f1
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/NfdLogActivity.java
@@ -0,0 +1,286 @@
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+/**
+ * @brief Display of NfdService's logcat output for easy debugging
+ */
+public class NfdLogActivity extends ActionBarActivity {
+
+  @Override
+  public boolean onCreateOptionsMenu(Menu menu) {
+    getMenuInflater().inflate(R.menu.menu_log, menu);
+    return true;
+  }
+
+  @Override
+  public boolean onOptionsItemSelected(MenuItem item) {
+    switch (item.getItemId()) {
+    case R.id.action_log_settings:
+      startActivity(new Intent(this, NfdLogSettingsActivity.class));
+      return true;
+    default:
+      return super.onOptionsItemSelected(item);
+    }
+  }
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_log);
+
+    // Get UI Elements
+    m_logListView = (ListView) findViewById(R.id.log_output);
+    m_logListAdapter = new LogListAdapter(getLayoutInflater(), s_logMaxLines);
+    m_logListView.setAdapter(m_logListAdapter);
+  }
+
+  @Override
+  protected void onResume() {
+    super.onResume();
+    startLogging();
+  }
+
+  @Override
+  protected void onPause() {
+    super.onPause();
+    stopLogging();
+  }
+
+  @Override
+  protected void onDestroy() {
+    super.onDestroy();
+    G.Log("LogActivity::onDestroy()");
+  }
+
+  /**
+   * @brief Starts logging by spawning a new thread to capture logs.
+   */
+  private void startLogging() {
+    // Clear output, update UI and get tag arguments
+    clearLogOutput();
+    appendLogText(getString(R.string.loading_logger));
+    m_tagArguments = NfdLogTagUtil.getTags(this);
+
+    new Thread(){
+      @Override
+      public void run() {
+        captureLog();
+        G.Log("Thread done capturing logs.");
+      }
+    }.start();
+  }
+
+  /**
+   * @brief Stops the logging by killing the process running logcat.
+   */
+  private void stopLogging() {
+    // Kill process
+    m_logProcess.destroy();
+  }
+
+  /**
+   * @brief Clear log adapter and update UI.
+   */
+  private void clearLogOutput() {
+    m_logListAdapter.clearMessages();
+  }
+
+  /**
+   * @brief Convenience method to append a message to the log output
+   * and scroll to the bottom of the log.
+   *
+   * @param message String message to be posted to the text view.
+   */
+  private void appendLogText(String message) {
+    m_logListAdapter.addMessage(message);
+    m_logListView.setSelection(m_logListAdapter.getCount() - 1);
+  }
+
+  /**
+   * @brief Convenience method to capture the output from logcat.
+   */
+  private void captureLog() {
+    try {
+      /**
+       * NOTE: The use of the 'time' log output format is hard
+       * coded for now. This option is similar to that of the
+       * log output that is seen in Android Studio and Eclipse.
+       *
+       * In the future this value can be user configurable or
+       * placed in the log preference settings.
+       */
+      // Build command for execution
+      String cmd = String.format("%s -v time %s *:S",
+        "logcat",
+        m_tagArguments);
+
+      G.Log("LogCat Command: " + cmd);
+
+      m_logProcess =  Runtime.getRuntime().exec(cmd);
+      BufferedReader in = new BufferedReader(
+        new InputStreamReader(m_logProcess.getInputStream()));
+
+      String line;
+      while ((line = in.readLine()) != null) {
+        final String message = line;
+        runOnUiThread(new Runnable() {
+          @Override
+          public void run() {
+            appendLogText(message);
+          }
+        });
+      }
+
+      // Wait for process to join this thread
+      m_logProcess.waitFor();
+    } catch (IOException e) {
+      G.Log("captureLog(): " + e);
+    } catch (InterruptedException e) {
+      G.Log("captureLog(): " + e);
+    }
+  }
+
+  /**
+   * @brief Custom LogListAdapter to limit the number of log lines that
+   * is being stored and displayed.
+   */
+  private static class LogListAdapter extends BaseAdapter {
+
+    /**
+     * @brief Create a ListView compatible adapter with an
+     * upper bound on the maximum number of entries that will
+     * be displayed in the ListView.
+     *
+     * @param maxLines Maximum number of entries allowed in
+     *                 the ListView for this adapter.
+     */
+    public LogListAdapter(LayoutInflater layoutInflater, int maxLines) {
+      m_data = new ArrayList<String>();
+      m_layoutInflater = layoutInflater;
+      m_maxLines = maxLines;
+    }
+
+    /**
+     * @brief Add a message to be displayed in the log's list view.
+     *
+     * @param message Message to be added to the underlying data store
+     *                and displayed on thi UI.
+     */
+    public void addMessage(String message) {
+      if (m_data.size() == m_maxLines) {
+        m_data.get(0);
+      }
+
+      m_data.add(message);
+      notifyDataSetChanged();
+    }
+
+    /**
+     * @brief Convenience method to clear all messages from the underlying
+     * data store and update the UI.
+     */
+    public void clearMessages() {
+      m_data.clear();
+      this.notifyDataSetChanged();
+    }
+
+    @Override
+    public int getCount() {
+      return m_data.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+      return m_data.get(position);
+    }
+
+    @Override
+    public long getItemId(int position) {
+      return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+      LogEntryViewHolder holder;
+
+      if (convertView == null) {
+        holder = new LogEntryViewHolder();
+
+        convertView = m_layoutInflater.inflate(R.layout.log_item, null);
+        convertView.setTag(holder);
+
+        holder.logLineTextView = (TextView) convertView.findViewById(R.id.log_line);
+      } else {
+        holder = (LogEntryViewHolder) convertView.getTag();
+      }
+
+      holder.logLineTextView.setText(m_data.get(position));
+      return convertView;
+    }
+
+    /** Underlying message data store for log messages*/
+    private ArrayList<String> m_data;
+
+    /** Layout inflater for inflating views */
+    private LayoutInflater m_layoutInflater;
+
+    /** Maximum number of log lines to display */
+    private int m_maxLines;
+  }
+
+  /**
+   * @brief Log entry view holder object for holding the output.
+   */
+  private static class LogEntryViewHolder {
+    public TextView logLineTextView;
+  }
+
+  /** @brief Maximum number of log lines to be displayed */
+  private static final int s_logMaxLines = 380;
+
+  /** @brief Process in which logcat is running in */
+  private Process m_logProcess;
+
+  /** @brief ListView for displaying log output in */
+  private ListView m_logListView;
+
+  /** @brief Customized ListAdapter for controlling log output */
+  private LogListAdapter m_logListAdapter;
+
+  /** @brief Tag argument to logcat */
+  private String m_tagArguments;
+}
diff --git a/app/src/main/java/net/named_data/nfd/NfdLogSettingsActivity.java b/app/src/main/java/net/named_data/nfd/NfdLogSettingsActivity.java
new file mode 100644
index 0000000..a86ca6f
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/NfdLogSettingsActivity.java
@@ -0,0 +1,249 @@
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceManager;
+import android.preference.PreferenceScreen;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NfdLogSettingsActivity extends Activity {
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    getFragmentManager().
+        beginTransaction().
+        replace(android.R.id.content, new NfdLogSettingsFragment()).
+        commit();
+  }
+
+  @SuppressLint("ValidFragment")
+  private static class NfdLogSettingsFragment extends PreferenceFragment {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+      super.onCreate(savedInstanceState);
+
+      // Load the preferences from an XML resource
+      addPreferencesFromResource(R.xml.pref_nfd_log);
+
+      PreferenceScreen preferenceScreen = getPreferenceScreen();
+
+      //// NOTE: This section of code demonstrates how a preference can
+      //// be added programmatically.
+      ////
+      // Creating and inserting a preference programmatically
+      // Preference customPreference = preferenceScreen.
+      //     findPreference(getString(R.string.pref_category_title_tags_key));
+      // if (customPreference instanceof PreferenceCategory) {
+      //   ListPreference listPreference = new ListPreference(getActivity());
+      //   listPreference.setKey("NFDTest_Key");
+      //   listPreference.setTitle("NFDTest");
+      //   listPreference.setDefaultValue("I");
+      //   listPreference.setNegativeButtonText(null);
+      //   listPreference.setDialogTitle("NFDTest");
+
+      //   String [] keys = getResources().getStringArray(R.array.pref_log_levels);
+      //   String [] values = getResources().getStringArray(R.array.pref_log_level_values);
+
+      //   listPreference.setEntries(keys);
+      //   listPreference.setEntryValues(values);
+
+      //   ((PreferenceCategory) customPreference).addPreference(listPreference);
+      // }
+
+      // Collect all Preference in the hierarchy
+      m_tagListPreferences = new ArrayList<ListPreference>();
+      extractPreferences(m_tagListPreferences,
+          (PreferenceGroup) preferenceScreen.
+              findPreference(getString(R.string.pref_category_title_tags_key)));
+
+      // Set all preference setting
+      m_resetPreference = preferenceScreen.
+          findPreference(getString(R.string.pref_tags_log_level_key));
+    }
+
+    @Override
+    public void onResume() {
+      super.onResume();
+      registerListeners();
+    }
+
+    @Override
+    public void onPause() {
+      super.onPause();
+      unregisterPreferenceListeners();
+      saveTagArguments();
+    }
+
+    /**
+     * @brief Convenience method to register preference listeners.
+     */
+    private void registerListeners() {
+      for (Preference p : m_tagListPreferences) {
+        if (p instanceof ListPreference) {
+          registerPreferenceListener(p);
+        }
+      }
+
+      m_resetPreference.setOnPreferenceChangeListener(m_setAllPreferenceChangeListener);
+    }
+
+    /**
+     * @brief Convenience method to unregister preference listeners.
+     */
+    private void unregisterPreferenceListeners() {
+      for (Preference p : m_tagListPreferences) {
+        if (p instanceof ListPreference) {
+          unregisterPreferenceListener(p);
+        }
+      }
+
+      m_resetPreference.setOnPreferenceChangeListener(null);
+    }
+
+    /**
+     * @brief Register preference listener and fire an update.
+     *
+     * @param preference Preference to register listener.
+     */
+    private void registerPreferenceListener(Preference preference) {
+      // Attach listener
+      preference.setOnPreferenceChangeListener(m_tagPreferenceChangeListener);
+
+      // Trigger update
+      m_tagPreferenceChangeListener.onPreferenceChange(preference,
+          PreferenceManager.
+              getDefaultSharedPreferences(preference.getContext()).
+              getString(preference.getKey(), ""));
+    }
+
+    /**
+     * @brief Unregister preference listener for the given preference.
+     *
+     * @param preference Preference to unregister listener.
+     */
+    private void unregisterPreferenceListener(Preference preference) {
+      // Remove listener
+      preference.setOnPreferenceChangeListener(null);
+    }
+
+    /**
+     * @brief Convenience method that extracts all list preferences within a hierarchy
+     * recursively.
+     *
+     * @param list List to add preference to
+     * @param preferenceGroup Root preference group to begin search from
+     */
+    private void extractPreferences(List<ListPreference> list, PreferenceGroup preferenceGroup) {
+      for (int i = 0; i < preferenceGroup.getPreferenceCount(); i++) {
+        final Preference preference = preferenceGroup.getPreference(i);
+
+        if (preference instanceof ListPreference) {
+          list.add((ListPreference) preference);
+        } else if (preference instanceof PreferenceGroup) {
+          extractPreferences(list, (PreferenceGroup) preference);
+        }
+      }
+    }
+
+    /**
+     * @brief Save tag arguments for quick retrieval.
+     */
+    private void saveTagArguments() {
+      NfdLogTagUtil.TagBuilder tagBuilder = NfdLogTagUtil.TagBuilder.getTagBuilder();
+
+      for (Preference p : m_tagListPreferences) {
+        if (p instanceof ListPreference) {
+          ListPreference listPreference = (ListPreference) p;
+          tagBuilder.addTag(listPreference.getTitle(), listPreference.getValue());
+        }
+      }
+
+      NfdLogTagUtil.saveTags(getActivity(), tagBuilder.generateTagString());
+    }
+
+    /**
+     * @brief Convenience method to change all tags' log level to the
+     * given logLevel.
+     *
+     * @param logLevel Target log level to set to.
+     */
+    private void setAllTagPreferences(String logLevel) {
+      for (ListPreference preference : m_tagListPreferences) {
+        preference.setValue(logLevel);
+        m_tagPreferenceChangeListener.onPreferenceChange(preference, logLevel);
+      }
+    }
+
+    /** List of preferences for registering handlers */
+    private List<ListPreference> m_tagListPreferences;
+
+    /** Reset log level preference */
+    private Preference m_resetPreference;
+
+    /**
+     * @brief Change listener that updates the summary text of the tag preferences.
+     */
+    private Preference.OnPreferenceChangeListener m_tagPreferenceChangeListener
+        = new Preference.OnPreferenceChangeListener() {
+      @Override
+      public boolean onPreferenceChange(Preference preference, Object value) {
+        // Get value of preference setting as a String
+        String displayString = value.toString();
+
+        // Deal with ListPreference
+        if (preference instanceof ListPreference) {
+          // Get display value
+          ListPreference listPreference = (ListPreference) preference;
+          int offset = listPreference.findIndexOfValue(displayString);
+          displayString = (offset != -1) ?
+              (String) listPreference.getEntries()[offset] :
+              null;
+        }
+
+        // Update UI
+        preference.setSummary(displayString);
+        return true;
+      }
+    };
+
+    /**
+     * @bried Change listener that resets all preference tags' log levels.
+     */
+    private Preference.OnPreferenceChangeListener m_setAllPreferenceChangeListener
+        = new Preference.OnPreferenceChangeListener() {
+      @Override
+      public boolean onPreferenceChange(Preference preference, Object value) {
+        setAllTagPreferences(value.toString());
+        return true;
+      }
+    };
+  }
+}
diff --git a/app/src/main/java/net/named_data/nfd/NfdLogTagUtil.java b/app/src/main/java/net/named_data/nfd/NfdLogTagUtil.java
new file mode 100644
index 0000000..34d81ef
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/NfdLogTagUtil.java
@@ -0,0 +1,179 @@
+/**
+ * Copyright (c) 2015 Regents of the University of California
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ *
+ * NFD Android is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.named_data.nfd;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @brief Utility class to generate and retrieve log tags
+ */
+public class NfdLogTagUtil {
+
+  /**
+   * @brief Get tags that are stored for the given application's context.
+   *
+   * The method returns a string representation of tags that should be displayed
+   * to the UI. These tags and their output levels have been saved by the settings
+   * UI in the given context.
+   *
+   * An example of the return string is as such:
+   *
+   * <pre>
+   *    NFDService:S Strategy:S TcpChannel:S TcpFactory:S TcpLocalFace:S UdpFactory:S *:S
+   * </pre>
+   *
+   * @param context Current application context to retrieve log tags for
+   * @return String representation of log tags ready for use as arguments
+   *         to logcat.
+   */
+  public static String getTags(Context context) {
+    SharedPreferences preferences = context.getSharedPreferences(
+        PREFERENCE_NFD_TAGS_FILENAME, Context.MODE_PRIVATE);
+    String tagsString = preferences.getString(PREFERENCE_NFD_TAGS_KEY, "");
+
+    G.Log("loadCommand(): " + tagsString);
+
+    return tagsString;
+  }
+
+  /**
+   * @brief Save tags as a string to the current context's preference.
+   *
+   * An example of a tag string that should be saved is shown as follows:
+   *
+   * <pre>
+   *    NFDService:S Strategy:S TcpChannel:S TcpFactory:S TcpLocalFace:S UdpFactory:S *:S
+   * </pre>
+   *
+   * NdfLogTagUtil.TagBuilder provides convenient methods to generate tag strings.
+   *
+   * @param context Current application context to save tag string to
+   * @param tagsString String representation of the tags to be saved
+   */
+  public static void saveTags(Context context, String tagsString) {
+    // Save preferred log level
+    SharedPreferences.Editor editor
+        = context.getSharedPreferences(
+        PREFERENCE_NFD_TAGS_FILENAME, Context.MODE_PRIVATE).edit();
+    editor.putString(PREFERENCE_NFD_TAGS_KEY, tagsString);
+    editor.commit();
+
+    G.Log("saveTags(): " + tagsString);
+  }
+
+  /**
+   * @brief Convenience class to create and generate tags for use as arguments
+   * to logcat.
+   */
+  public static class TagBuilder {
+
+    private TagBuilder() {
+      m_tagMap = new HashMap<CharSequence, CharSequence>();
+    }
+
+    /**
+     * @brief Get a new instance of a TagBuilder object.
+     *
+     * @return New TagBuilder ojbect for use.
+     */
+    public static TagBuilder getTagBuilder() {
+      return new TagBuilder();
+    }
+
+    /**
+     * @bried Add a tag with an associated log level value.
+     *
+     * Tag can be any tag for the logger to filter.
+     *
+     * The log level should be one of the following form:
+     * <pre>
+     *     Log Level | Meaning
+     *     ===================
+     *       V       : Verbose
+     *       D       : Debug
+     *       I       : Info
+     *       W       : Warning
+     *       E       : Error
+     *       F       : Fatal
+     *       S       : Silent
+     * </pre>
+     *
+     * @param tag Tag for the logger to filter for.
+     * @param logLevel Log level for specified tag.
+     */
+    public void addTag(CharSequence tag, CharSequence logLevel) {
+      m_tagMap.put(tag, logLevel);
+    }
+
+    /**
+     * @brief Silence all tags that are not added to the current TagBuilder
+     * object.
+     */
+    public synchronized  void addSilenceNonRelatedTags() {
+      m_silenceNonRelatedTags = true;
+    }
+
+    /**
+     * @brief Generate a string representing all the tags to be filtered and the
+     * relevant log levels.
+     *
+     * An example of a string returned by this method is:
+     *
+     * <pre>
+     *    NFDService:S Strategy:S TcpChannel:S TcpFactory:S TcpLocalFace:S UdpFactory:S *:S
+     * </pre>
+     *
+     * @return String representation of the tags and their relevant log levels to be
+     * filtered.
+     */
+    public String generateTagString() {
+      ArrayList<String> tags = new ArrayList<String>();
+      for (Map.Entry<CharSequence, CharSequence> item : m_tagMap.entrySet()) {
+        tags.add(String.format("%s:%s", item.getKey(), item.getValue()));
+      }
+
+      Collections.sort(tags);
+
+      if (m_silenceNonRelatedTags) {
+        tags.add("*:S");
+      }
+
+      return TextUtils.join(" ", tags);
+    }
+
+    /** Mapping of tag and log levels */
+    private Map<CharSequence, CharSequence> m_tagMap;
+
+    /** Flag that determines if all non related tags should be silenced */
+    private boolean m_silenceNonRelatedTags;
+  }
+
+  /** @brief Preference filename */
+  private static final String PREFERENCE_NFD_TAGS_FILENAME = "NFD_TAGS_PREFERENCE_FILE";
+
+  /** @brief Key in SharedPreference that stores the string of tags */
+  private static final String PREFERENCE_NFD_TAGS_KEY = "NFD_TAGS";
+}
diff --git a/app/src/main/java/net/named_data/nfd/NfdMainActivity.java b/app/src/main/java/net/named_data/nfd/NfdMainActivity.java
index e9a5165..272b4b4 100644
--- a/app/src/main/java/net/named_data/nfd/NfdMainActivity.java
+++ b/app/src/main/java/net/named_data/nfd/NfdMainActivity.java
@@ -95,6 +95,10 @@
     toggleNfdState();
   }
 
+  public void launchLogActivity(View view) {
+    startActivity(new Intent(this, NfdLogActivity.class));
+  }
+
   /**
    * Thread safe way to start and stop the NFD through
    * the UI Button.
diff --git a/app/src/main/java/net/named_data/nfd/NfdSettingsActivity.java b/app/src/main/java/net/named_data/nfd/NfdSettingsActivity.java
deleted file mode 100644
index 99cbfa0..0000000
--- a/app/src/main/java/net/named_data/nfd/NfdSettingsActivity.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
-import android.preference.PreferenceCategory;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceManager;
-import android.preference.RingtonePreference;
-import android.text.TextUtils;
-
-import java.util.List;
-
-/**
- * A {@link PreferenceActivity} that presents a set of application settings. On
- * handset devices, settings are presented as a single list. On tablets,
- * settings are split by category, with category headers shown to the left of
- * the list of settings.
- * <p/>
- * See <a href="http://developer.android.com/design/patterns/settings.html">
- * Android Design: Settings</a> for design guidelines and the <a
- * href="http://developer.android.com/guide/topics/ui/settings.html">Settings
- * API Guide</a> for more information on developing a Settings UI.
- */
-public class NfdSettingsActivity extends PreferenceActivity
-{
-  /**
-   * Determines whether to always show the simplified settings UI, where
-   * settings are presented in a single list. When false, settings are shown
-   * as a master/detail two-pane view on tablets. When true, a single pane is
-   * shown on tablets.
-   */
-  private static final boolean ALWAYS_SIMPLE_PREFS = false;
-
-
-  @Override
-  protected void onPostCreate(Bundle savedInstanceState)
-  {
-    super.onPostCreate(savedInstanceState);
-
-    setupSimplePreferencesScreen();
-  }
-
-  /**
-   * Shows the simplified settings UI if the device configuration if the
-   * device configuration dictates that a simplified, single-pane UI should be
-   * shown.
-   */
-  private void setupSimplePreferencesScreen()
-  {
-    if (!isSimplePreferences(this)) {
-      return;
-    }
-
-    // In the simplified UI, fragments are not used at all and we instead
-    // use the older PreferenceActivity APIs.
-
-    // Add 'general' preferences.
-    addPreferencesFromResource(R.xml.pref_general);
-
-    // Add 'notifications' preferences, and a corresponding header.
-    PreferenceCategory fakeHeader = new PreferenceCategory(this);
-    fakeHeader.setTitle(R.string.pref_header_notifications);
-    getPreferenceScreen().addPreference(fakeHeader);
-    addPreferencesFromResource(R.xml.pref_notification);
-
-    // Add 'data and sync' preferences, and a corresponding header.
-    fakeHeader = new PreferenceCategory(this);
-    fakeHeader.setTitle(R.string.pref_header_data_sync);
-    getPreferenceScreen().addPreference(fakeHeader);
-    addPreferencesFromResource(R.xml.pref_data_sync);
-
-    // Bind the summaries of EditText/List/Dialog/Ringtone preferences to
-    // their values. When their values change, their summaries are updated
-    // to reflect the new value, per the Android Design guidelines.
-    bindPreferenceSummaryToValue(findPreference("example_text"));
-    bindPreferenceSummaryToValue(findPreference("example_list"));
-    bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
-    bindPreferenceSummaryToValue(findPreference("sync_frequency"));
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public boolean onIsMultiPane()
-  {
-    return isXLargeTablet(this) && !isSimplePreferences(this);
-  }
-
-  /**
-   * Helper method to determine if the device has an extra-large screen. For
-   * example, 10" tablets are extra-large.
-   */
-  private static boolean isXLargeTablet(Context context)
-  {
-    return (context.getResources().getConfiguration().screenLayout &
-      Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
-  }
-
-  /**
-   * Determines whether the simplified settings UI should be shown. This is
-   * true if this is forced via {@link #ALWAYS_SIMPLE_PREFS}, or the device
-   * doesn't have newer APIs like {@link PreferenceFragment}, or the device
-   * doesn't have an extra-large screen. In these cases, a single-pane
-   * "simplified" settings UI should be shown.
-   */
-  private static boolean isSimplePreferences(Context context)
-  {
-    return ALWAYS_SIMPLE_PREFS ||
-      Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ||
-      !isXLargeTablet(context);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-  public void onBuildHeaders(List<Header> target)
-  {
-    if (!isSimplePreferences(this)) {
-      loadHeadersFromResource(R.xml.pref_headers, target);
-    }
-  }
-
-  /**
-   * A preference value change listener that updates the preference's summary
-   * to reflect its new value.
-   */
-  private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener()
-  {
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object value)
-    {
-      String stringValue = value.toString();
-
-      if (preference instanceof ListPreference) {
-        // For list preferences, look up the correct display value in
-        // the preference's 'entries' list.
-        ListPreference listPreference = (ListPreference) preference;
-        int index = listPreference.findIndexOfValue(stringValue);
-
-        // Set the summary to reflect the new value.
-        preference.setSummary(
-          index >= 0 ?
-            listPreference.getEntries()[index] :
-            null);
-
-      } else if (preference instanceof RingtonePreference) {
-        // For ringtone preferences, look up the correct display value
-        // using RingtoneManager.
-        if (TextUtils.isEmpty(stringValue)) {
-          // Empty values correspond to 'silent' (no ringtone).
-          preference.setSummary(R.string.pref_ringtone_silent);
-
-        } else {
-          Ringtone ringtone = RingtoneManager.getRingtone(
-            preference.getContext(), Uri.parse(stringValue));
-
-          if (ringtone == null) {
-            // Clear the summary if there was a lookup error.
-            preference.setSummary(null);
-          } else {
-            // Set the summary to reflect the new ringtone display
-            // name.
-            String name = ringtone.getTitle(preference.getContext());
-            preference.setSummary(name);
-          }
-        }
-
-      } else {
-        // For all other preferences, set the summary to the value's
-        // simple string representation.
-        preference.setSummary(stringValue);
-      }
-      return true;
-    }
-  };
-
-  /**
-   * Binds a preference's summary to its value. More specifically, when the
-   * preference's value is changed, its summary (line of text below the
-   * preference title) is updated to reflect the value. The summary is also
-   * immediately updated upon calling this method. The exact display format is
-   * dependent on the type of preference.
-   *
-   * @see #sBindPreferenceSummaryToValueListener
-   */
-  private static void bindPreferenceSummaryToValue(Preference preference)
-  {
-    // Set the listener to watch for value changes.
-    preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
-
-    // Trigger the listener immediately with the preference's
-    // current value.
-    sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
-      PreferenceManager
-        .getDefaultSharedPreferences(preference.getContext())
-        .getString(preference.getKey(), ""));
-  }
-
-  /**
-   * This fragment shows general preferences only. It is used when the
-   * activity is showing a two-pane settings UI.
-   */
-  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-  public static class GeneralPreferenceFragment extends PreferenceFragment
-  {
-    @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
-      super.onCreate(savedInstanceState);
-      addPreferencesFromResource(R.xml.pref_general);
-
-      // Bind the summaries of EditText/List/Dialog/Ringtone preferences
-      // to their values. When their values change, their summaries are
-      // updated to reflect the new value, per the Android Design
-      // guidelines.
-      bindPreferenceSummaryToValue(findPreference("example_text"));
-      bindPreferenceSummaryToValue(findPreference("example_list"));
-    }
-  }
-
-  /**
-   * This fragment shows notification preferences only. It is used when the
-   * activity is showing a two-pane settings UI.
-   */
-  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-  public static class NotificationPreferenceFragment extends PreferenceFragment
-  {
-    @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
-      super.onCreate(savedInstanceState);
-      addPreferencesFromResource(R.xml.pref_notification);
-
-      // Bind the summaries of EditText/List/Dialog/Ringtone preferences
-      // to their values. When their values change, their summaries are
-      // updated to reflect the new value, per the Android Design
-      // guidelines.
-      bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
-    }
-  }
-
-  /**
-   * This fragment shows data and sync preferences only. It is used when the
-   * activity is showing a two-pane settings UI.
-   */
-  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-  public static class DataSyncPreferenceFragment extends PreferenceFragment
-  {
-    @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
-      super.onCreate(savedInstanceState);
-      addPreferencesFromResource(R.xml.pref_data_sync);
-
-      // Bind the summaries of EditText/List/Dialog/Ringtone preferences
-      // to their values. When their values change, their summaries are
-      // updated to reflect the new value, per the Android Design
-      // guidelines.
-      bindPreferenceSummaryToValue(findPreference("sync_frequency"));
-    }
-  }
-}
diff --git a/app/src/main/res/layout/activity_log.xml b/app/src/main/res/layout/activity_log.xml
new file mode 100644
index 0000000..93f58f0
--- /dev/null
+++ b/app/src/main/res/layout/activity_log.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <ListView
+        android:id="@+id/log_output"
+        android:layout_weight="1"
+        android:layout_width="match_parent"
+        android:layout_height="0dp">
+    </ListView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index cb17103..ee8e9b5 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,10 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
-    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
     android:paddingRight="@dimen/activity_horizontal_margin"
     android:paddingTop="@dimen/activity_vertical_margin"
-    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    tools:context=".MainActivity">
 
     <Button
         android:id="@+id/nfd_button"
@@ -12,6 +15,14 @@
         android:layout_height="wrap_content"
         android:onClick="toggleNfdState"
         android:enabled="false"
-        android:text="Checking on NFD Service ..." />
+        android:text="@string/checking_on_nfd" />
+
+    <Button
+        android:id="@+id/launch_logger"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/nfd_button"
+        android:onClick="launchLogActivity"
+        android:text="@string/launch_logger" />
 
 </RelativeLayout>
diff --git a/app/src/main/res/layout/log_item.xml b/app/src/main/res/layout/log_item.xml
new file mode 100644
index 0000000..6b0b6e1
--- /dev/null
+++ b/app/src/main/res/layout/log_item.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/log_line"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_log.xml b/app/src/main/res/menu/menu_log.xml
new file mode 100644
index 0000000..b749bbe
--- /dev/null
+++ b/app/src/main/res/menu/menu_log.xml
@@ -0,0 +1,12 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context=".NfdLogActivity">
+
+    <item
+        android:id="@+id/action_log_settings"
+        android:title="@string/log_settings"
+        android:orderInCategory="100"
+        app:showAsAction="never" />
+
+</menu>
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index b1cb908..e80d2f5 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -1,6 +1,11 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
-    <item android:id="@+id/action_settings" android:title="@string/action_settings"
-        android:orderInCategory="100" app:showAsAction="never" />
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:context=".MainActivity">
+
+    <item android:id="@+id/action_settings"
+        android:title="@string/action_settings"
+        android:orderInCategory="100"
+        app:showAsAction="never" />
+
 </menu>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 601b73f..4e48935 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -8,4 +8,18 @@
     <string name="stopping_nfd">Stopping NFD ...</string>
     <string name="starting_nfd">Starting NFD ...</string>
     <string name="reconnect_to_nfd">Reconnecting to NFD Service</string>
+    <string name="loading_logger">Loading logger ...</string>
+    <string name="log_level_verbose">Verbose</string>
+    <string name="log_level_debug">Debug</string>
+    <string name="log_level_info">Info</string>
+    <string name="log_level_warn">Warn</string>
+    <string name="log_level_error">Error</string>
+    <string name="log_level_assert">Assert</string>
+    <string name="log_level_fatal">Fatal</string>
+    <string name="log_level_silent">Silent</string>
+    <string name="nfd_logger">NFD Logger</string>
+    <string name="log_settings">Log Settings</string>
+    <string name="nfd_log_settings">NFD Log Settings</string>
+    <string name="checking_on_nfd">Checking on NFD Service ...</string>
+    <string name="launch_logger">Launch Logger!</string>
 </resources>
diff --git a/app/src/main/res/values/strings_activity_nfd_settings.xml b/app/src/main/res/values/strings_activity_nfd_settings.xml
index 2686b04..e55844b 100644
--- a/app/src/main/res/values/strings_activity_nfd_settings.xml
+++ b/app/src/main/res/values/strings_activity_nfd_settings.xml
@@ -1,60 +1,79 @@
 <resources>
 
-    <!-- Strings related to Settings -->
+    <!-- Strings related to NFD Log Settings -->
 
-    <!-- Example General settings -->
-    <string name="pref_header_general">General</string>
+    <!-- General -->
+    <string name="pref_category_title_general">General</string>
+    <string name="pref_category_title_general_key">General_Key</string>
 
-    <string name="pref_title_social_recommendations">Enable social recommendations</string>
-    <string name="pref_description_social_recommendations">Recommendations for people to contact
-        based on your message history
-    </string>
+    <!-- Set all tags log level -->
+    <string name="pref_tags_log_level_title_key">All_Log_Levels_Key</string>
+    <string name="pref_tags_log_level_title">All Log Levels</string>
+    <string name="pref_tags_log_level_key">Reset_All_Tags_Log_Level_Key</string>
+    <string name="pref_tags_log_level">Reset</string>
 
-    <string name="pref_title_display_name">Display name</string>
-    <string name="pref_default_display_name">John Smith</string>
+    <!-- Tags -->
+    <string name="pref_category_title_tags">NFD Tags &amp; Log Levels</string>
+    <string name="pref_category_title_tags_key">NFD_Tags_Key</string>
 
-    <string name="pref_title_add_friends_to_messages">Add friends to messages</string>
-    <string-array name="pref_example_list_titles">
-        <item>Always</item>
-        <item>When possible</item>
-        <item>Never</item>
-    </string-array>
-    <string-array name="pref_example_list_values">
-        <item>1</item>
-        <item>0</item>
-        <item>-1</item>
+    <!-- Tag Display Names -->
+    <string name="pref_tag_commandvalidator">CommandValidator</string>
+    <string name="pref_tag_facemanager">FaceManager</string>
+    <string name="pref_tag_facetable">FaceTable</string>
+    <string name="pref_tag_fibmanager">FibManager</string>
+    <string name="pref_tag_generalconfigsection">GeneralConfigSection</string>
+    <string name="pref_tag_internalface">InternalFace</string>
+    <string name="pref_tag_managerbase">ManagerBase</string>
+    <string name="pref_tag_privilegehelper">PrivilegeHelper</string>
+    <string name="pref_tag_remoteregistrator">RemoteRegistrator</string>
+    <string name="pref_tag_ribmanager">RibManager</string>
+    <string name="pref_tag_strategy">Strategy</string>
+    <string name="pref_tag_strategychoice">StrategyChoice</string>
+    <string name="pref_tag_tablesconfigsection">TablesConfigSection</string>
+    <string name="pref_tag_tcpchannel">TcpChannel</string>
+    <string name="pref_tag_tcpfactory">TcpFactory</string>
+    <string name="pref_tag_tcplocalface">TcpLocalFace</string>
+    <string name="pref_tag_udpfactory">UdpFactory</string>
+
+    <!-- Tag Keys -->
+    <string name="pref_tag_commandvalidator_key">CommandValidator</string>
+    <string name="pref_tag_facemanager_key">FaceManager</string>
+    <string name="pref_tag_facetable_key">FaceTable</string>
+    <string name="pref_tag_fibmanager_key">FibManager</string>
+    <string name="pref_tag_generalconfigsection_key">GeneralConfigSection</string>
+    <string name="pref_tag_internalface_key">InternalFace</string>
+    <string name="pref_tag_managerbase_key">ManagerBase</string>
+    <string name="pref_tag_privilegehelper_key">PrivilegeHelper</string>
+    <string name="pref_tag_remoteregistrator_key">RemoteRegistrator</string>
+    <string name="pref_tag_ribmanager_key">RibManager</string>
+    <string name="pref_tag_strategy_key">Strategy</string>
+    <string name="pref_tag_strategychoice_key">StrategyChoice</string>
+    <string name="pref_tag_tablesconfigsection_key">TablesConfigSection</string>
+    <string name="pref_tag_tcpchannel_key">TcpChannel</string>
+    <string name="pref_tag_tcpfactory_key">TcpFactory</string>
+    <string name="pref_tag_tcplocalface_key">TcpLocalFace</string>
+    <string name="pref_tag_udpfactory_key">UdpFactory</string>
+
+    <!-- Log Levels -->
+    <string-array name="pref_log_levels">
+        <item>Verbose</item>
+        <item>Debug</item>
+        <item>Info</item>
+        <item>Warn</item>
+        <item>Error</item>
+        <item>Fatal</item>
+        <item>Silent</item>
     </string-array>
 
-    <!-- Example settings for Data & Sync -->
-    <string name="pref_header_data_sync">Data &amp; sync</string>
-
-    <string name="pref_title_sync_frequency">Sync frequency</string>
-    <string-array name="pref_sync_frequency_titles">
-        <item>15 minutes</item>
-        <item>30 minutes</item>
-        <item>1 hour</item>
-        <item>3 hours</item>
-        <item>6 hours</item>
-        <item>Never</item>
-    </string-array>
-    <string-array name="pref_sync_frequency_values">
-        <item>15</item>
-        <item>30</item>
-        <item>60</item>
-        <item>180</item>
-        <item>360</item>
-        <item>-1</item>
+    <!-- Log Level Command Tag-->
+    <string-array name="pref_log_level_values">
+        <item>V</item>
+        <item>D</item>
+        <item>I</item>
+        <item>W</item>
+        <item>E</item>
+        <item>F</item>
+        <item>S</item>
     </string-array>
 
-    <string name="pref_title_system_sync_settings">System sync settings</string>
-
-    <!-- Example settings for Notifications -->
-    <string name="pref_header_notifications">Notifications</string>
-
-    <string name="pref_title_new_message_notifications">New message notifications</string>
-
-    <string name="pref_title_ringtone">Ringtone</string>
-    <string name="pref_ringtone_silent">Silent</string>
-
-    <string name="pref_title_vibrate">Vibrate</string>
 </resources>
diff --git a/app/src/main/res/xml/pref_data_sync.xml b/app/src/main/res/xml/pref_data_sync.xml
deleted file mode 100644
index ffda831..0000000
--- a/app/src/main/res/xml/pref_data_sync.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
-         dismiss it. -->
-    <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
-    <ListPreference
-        android:key="sync_frequency"
-        android:title="@string/pref_title_sync_frequency"
-        android:entries="@array/pref_sync_frequency_titles"
-        android:entryValues="@array/pref_sync_frequency_values"
-        android:defaultValue="180"
-        android:negativeButtonText="@null"
-        android:positiveButtonText="@null" />
-
-    <!-- This preference simply launches an intent when selected. Use this UI sparingly, per
-         design guidelines. -->
-    <Preference android:title="@string/pref_title_system_sync_settings">
-        <intent android:action="android.settings.SYNC_SETTINGS" />
-    </Preference>
-
-</PreferenceScreen>
diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml
deleted file mode 100644
index c49cbed..0000000
--- a/app/src/main/res/xml/pref_general.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <CheckBoxPreference
-        android:key="example_checkbox"
-        android:title="@string/pref_title_social_recommendations"
-        android:summary="@string/pref_description_social_recommendations"
-        android:defaultValue="true" />
-
-    <!-- NOTE: EditTextPreference accepts EditText attributes. -->
-    <!-- NOTE: EditTextPreference's summary should be set to its value by the activity code. -->
-    <EditTextPreference
-        android:key="example_text"
-        android:title="@string/pref_title_display_name"
-        android:defaultValue="@string/pref_default_display_name"
-        android:selectAllOnFocus="true"
-        android:inputType="textCapWords"
-        android:capitalize="words"
-        android:singleLine="true"
-        android:maxLines="1" />
-
-    <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
-         dismiss it. -->
-    <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
-    <ListPreference
-        android:key="example_list"
-        android:title="@string/pref_title_add_friends_to_messages"
-        android:defaultValue="-1"
-        android:entries="@array/pref_example_list_titles"
-        android:entryValues="@array/pref_example_list_values"
-        android:negativeButtonText="@null"
-        android:positiveButtonText="@null" />
-
-</PreferenceScreen>
diff --git a/app/src/main/res/xml/pref_headers.xml b/app/src/main/res/xml/pref_headers.xml
deleted file mode 100644
index f2af3f2..0000000
--- a/app/src/main/res/xml/pref_headers.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <!-- These settings headers are only used on tablets. -->
-
-    <header
-        android:fragment="nfd.named_data.net.nfd.NfdSettingsActivity$GeneralPreferenceFragment"
-        android:title="@string/pref_header_general"/>
-
-    <header
-        android:fragment="nfd.named_data.net.nfd.NfdSettingsActivity$NotificationPreferenceFragment"
-        android:title="@string/pref_header_notifications"/>
-
-    <header
-        android:fragment="nfd.named_data.net.nfd.NfdSettingsActivity$DataSyncPreferenceFragment"
-        android:title="@string/pref_header_data_sync"/>
-
-</preference-headers>
diff --git a/app/src/main/res/xml/pref_nfd_log.xml b/app/src/main/res/xml/pref_nfd_log.xml
new file mode 100644
index 0000000..37a0432
--- /dev/null
+++ b/app/src/main/res/xml/pref_nfd_log.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <PreferenceCategory
+        android:key="@string/pref_tags_log_level_title_key"
+        android:title="@string/pref_tags_log_level_title">
+
+        <ListPreference
+            android:key="@string/pref_tags_log_level_key"
+            android:title="@string/pref_tags_log_level"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="@string/pref_category_title_tags_key"
+        android:title="@string/pref_category_title_tags">
+
+        <ListPreference
+            android:key="@string/pref_tag_commandvalidator_key"
+            android:title="@string/pref_tag_commandvalidator"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_facemanager_key"
+            android:title="@string/pref_tag_facemanager"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_facetable_key"
+            android:title="@string/pref_tag_facetable"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_fibmanager_key"
+            android:title="@string/pref_tag_fibmanager"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_generalconfigsection_key"
+            android:title="@string/pref_tag_generalconfigsection"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_internalface_key"
+            android:title="@string/pref_tag_internalface"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_managerbase_key"
+            android:title="@string/pref_tag_managerbase"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_privilegehelper_key"
+            android:title="@string/pref_tag_privilegehelper"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_remoteregistrator_key"
+            android:title="@string/pref_tag_remoteregistrator"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_ribmanager_key"
+            android:title="@string/pref_tag_ribmanager"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_strategy_key"
+            android:title="@string/pref_tag_strategy"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_strategychoice_key"
+            android:title="@string/pref_tag_strategychoice"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_tablesconfigsection_key"
+            android:title="@string/pref_tag_tablesconfigsection"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_tcpchannel_key"
+            android:title="@string/pref_tag_tcpchannel"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_tcpfactory_key"
+            android:title="@string/pref_tag_tcpfactory"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_tcplocalface_key"
+            android:title="@string/pref_tag_tcplocalface"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+        <ListPreference
+            android:key="@string/pref_tag_udpfactory_key"
+            android:title="@string/pref_tag_udpfactory"
+            android:entries="@array/pref_log_levels"
+            android:entryValues="@array/pref_log_level_values"
+            android:defaultValue="I"
+            android:negativeButtonText="@null"
+            android:positiveButtonText="@null" />
+
+    </PreferenceCategory>
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/pref_notification.xml b/app/src/main/res/xml/pref_notification.xml
deleted file mode 100644
index b4b8cae..0000000
--- a/app/src/main/res/xml/pref_notification.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <!-- A 'parent' preference, which enables/disables child preferences (below)
-         when checked/unchecked. -->
-    <CheckBoxPreference
-        android:key="notifications_new_message"
-        android:title="@string/pref_title_new_message_notifications"
-        android:defaultValue="true" />
-
-    <!-- Allows the user to choose a ringtone in the 'notification' category. -->
-    <!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
-    <!-- NOTE: RingtonePreference's summary should be set to its value by the activity code. -->
-    <RingtonePreference
-        android:dependency="notifications_new_message"
-        android:key="notifications_new_message_ringtone"
-        android:title="@string/pref_title_ringtone"
-        android:ringtoneType="notification"
-        android:defaultValue="content://settings/system/notification_sound" />
-
-    <!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
-    <CheckBoxPreference
-        android:dependency="notifications_new_message"
-        android:key="notifications_new_message_vibrate"
-        android:title="@string/pref_title_vibrate"
-        android:defaultValue="true" />
-
-</PreferenceScreen>