Add permanent face and route management memchanism

Change-Id: I23e77358e89c49473445994b8bd0c2c425d0e1b0
Refs: #3443
diff --git a/app/src/main/java/net/named_data/nfd/FaceCreateDialogFragment.java b/app/src/main/java/net/named_data/nfd/FaceCreateDialogFragment.java
index 5e5340a..89223c1 100644
--- a/app/src/main/java/net/named_data/nfd/FaceCreateDialogFragment.java
+++ b/app/src/main/java/net/named_data/nfd/FaceCreateDialogFragment.java
@@ -28,12 +28,13 @@
 import android.support.v4.app.DialogFragment;
 import android.view.LayoutInflater;
 import android.view.WindowManager;
+import android.widget.CheckBox;
 import android.widget.EditText;
 
 public class FaceCreateDialogFragment extends DialogFragment {
   public static interface OnFaceCreateRequested {
     public void
-    createFace(String faceUri);
+    createFace(String faceUri, boolean isPermanent);
   }
 
   public static FaceCreateDialogFragment
@@ -54,7 +55,8 @@
           {
             EditText uriBox = (EditText) getDialog().findViewById(R.id.faceUri);
             String uri = uriBox.getText().toString();
-            ((OnFaceCreateRequested)getTargetFragment()).createFace(uri);
+            CheckBox permanent = (CheckBox) getDialog().findViewById(R.id.permanent);
+            ((OnFaceCreateRequested) getTargetFragment()).createFace(uri, permanent.isChecked());
           }
         })
       .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
diff --git a/app/src/main/java/net/named_data/nfd/FaceListFragment.java b/app/src/main/java/net/named_data/nfd/FaceListFragment.java
index 55d1860..4a605d1 100644
--- a/app/src/main/java/net/named_data/nfd/FaceListFragment.java
+++ b/app/src/main/java/net/named_data/nfd/FaceListFragment.java
@@ -24,9 +24,11 @@
 import android.content.Context;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.Handler;
 import android.support.annotation.Nullable;
 import android.support.v4.app.ListFragment;
 import android.util.Pair;
+import android.util.SparseArray;
 import android.view.ActionMode;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -41,11 +43,14 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.intel.jndn.management.ManagementException;
 import com.intel.jndn.management.types.FaceStatus;
 
+import net.named_data.jndn.Name;
 import net.named_data.jndn_xx.util.FaceUri;
 import net.named_data.nfd.utils.G;
 import net.named_data.nfd.utils.NfdcHelper;
+import net.named_data.nfd.utils.PermanentFaceUriAndRouteManager;
 
 import java.util.HashSet;
 import java.util.List;
@@ -231,9 +236,9 @@
 
   @Override
   public void
-  createFace(String faceUri)
+  createFace(String faceUri, boolean isPermanent)
   {
-    m_faceCreateAsyncTask = new FaceCreateAsyncTask(faceUri);
+    m_faceCreateAsyncTask = new FaceCreateAsyncTask(faceUri, isPermanent);
     m_faceCreateAsyncTask.execute();
   }
 
@@ -372,7 +377,7 @@
       NfdcHelper nfdcHelper = new NfdcHelper();
       List<FaceStatus> faceStatusList = null;
       try {
-        faceStatusList = nfdcHelper.faceList();
+        faceStatusList = nfdcHelper.faceList(getActivity().getApplicationContext());
       } catch (Exception e) {
         returnException = e;
       }
@@ -423,16 +428,27 @@
 
       NfdcHelper nfdcHelper = new NfdcHelper();
       try {
+        Context context = getActivity().getApplicationContext();
+        SparseArray<FaceStatus> faceSparseArray = nfdcHelper.faceListAsSparseArray(context);
         for (Set<Integer> faces : params) {
           for (int faceId : faces) {
-            nfdcHelper.faceDestroy(faceId);
+            FaceStatus one = faceSparseArray.get(faceId, null);
+            if (null != one){
+              removeRoutesOfFace(nfdcHelper, faceId);
+              // TODO: what if face was saved but is not in the face list? Is it possible?
+              PermanentFaceUriAndRouteManager.deletePermanentFaceUri(
+                  context,
+                  one.getRemoteUri()
+              );
+              PermanentFaceUriAndRouteManager.deletePermanentFaceId(context, faceId);
+              nfdcHelper.faceDestroy(faceId);
+            }
           }
         }
       } catch (Exception e) {
         retval = e;
       }
       nfdcHelper.shutdown();
-
       return retval;
     }
 
@@ -455,15 +471,30 @@
       }
       else {
         // Reload face list
-        retrieveFaceList();
+        m_timeoutHandler.postDelayed(new Runnable() {
+          @Override
+          public void run() {
+            retrieveFaceList();
+          }
+        }, 500);
       }
     }
   }
 
+  private void
+  removeRoutesOfFace(NfdcHelper nfdcHelper, int faceId) throws ManagementException {
+    SparseArray<Set<Name>> faceIdPrefixSparseArray = nfdcHelper.ribAsFaceIdPrefixNameArray();
+    Set<Name> prefixes = faceIdPrefixSparseArray.get(faceId, null);
+    if (null != prefixes){
+      RouteListFragment.removeRouteSyncs(getActivity().getApplicationContext(), faceId, prefixes);
+    }
+  }
+
   private class FaceCreateAsyncTask extends AsyncTask<Void, Void, String> {
-    public FaceCreateAsyncTask(String faceUri)
+    public FaceCreateAsyncTask(String faceUri, boolean isPermanent)
     {
       m_faceUri = faceUri;
+      m_isPermanent = isPermanent;
     }
 
     @Override
@@ -472,7 +503,15 @@
     {
       try {
         NfdcHelper nfdcHelper = new NfdcHelper();
+        Context context = getActivity().getApplicationContext();
         int faceId = nfdcHelper.faceCreate(m_faceUri);
+        if (m_isPermanent) {
+          PermanentFaceUriAndRouteManager.addPermanentFaceUri(
+                                             context,
+                                             NfdcHelper.formatFaceUri(m_faceUri)
+                                         );
+          PermanentFaceUriAndRouteManager.addPermanentFaceId(context, faceId);
+        }
         nfdcHelper.shutdown();
         return "OK. Face id: " + String.valueOf(faceId);
       }
@@ -514,6 +553,7 @@
     ///////////////////////////////////////////////////////////////////////////
 
     private String m_faceUri;
+    private boolean m_isPermanent;
   }
   /////////////////////////////////////////////////////////////////////////
 
@@ -548,4 +588,7 @@
   private ProgressBar m_reloadingListProgressBar;
 
   private FaceListAdapter m_faceListAdapter;
+
+  private Handler m_timeoutHandler = new Handler();
+
 }
diff --git a/app/src/main/java/net/named_data/nfd/RouteCreateDialogFragment.java b/app/src/main/java/net/named_data/nfd/RouteCreateDialogFragment.java
index a6e0887..8e09e55 100644
--- a/app/src/main/java/net/named_data/nfd/RouteCreateDialogFragment.java
+++ b/app/src/main/java/net/named_data/nfd/RouteCreateDialogFragment.java
@@ -27,6 +27,7 @@
 import android.support.v4.app.DialogFragment;
 import android.view.LayoutInflater;
 import android.view.WindowManager;
+import android.widget.CheckBox;
 import android.widget.EditText;
 
 import net.named_data.jndn.Name;
@@ -35,7 +36,7 @@
 {
   public static interface OnRouteCreateRequested {
     public void
-    createRoute(Name prefix, String faceUri);
+    createRoute(Name prefix, String faceUri, boolean isPermanent);
   }
 
   public static RouteCreateDialogFragment
@@ -59,7 +60,8 @@
           final String prefix = prefixBox.getText().toString();
           final String uri = uriBox.getText().toString();
 
-          ((OnRouteCreateRequested)getTargetFragment()).createRoute(new Name(prefix), uri);
+          CheckBox permanent = (CheckBox) getDialog().findViewById(R.id.permanent);
+          ((OnRouteCreateRequested)getTargetFragment()).createRoute(new Name(prefix), uri, permanent.isChecked());
         }
       })
       .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
diff --git a/app/src/main/java/net/named_data/nfd/RouteInfoFragment.java b/app/src/main/java/net/named_data/nfd/RouteInfoFragment.java
index 162f80b..4e77718 100644
--- a/app/src/main/java/net/named_data/nfd/RouteInfoFragment.java
+++ b/app/src/main/java/net/named_data/nfd/RouteInfoFragment.java
@@ -23,6 +23,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.intel.jndn.management.ManagementException;
 import com.intel.jndn.management.enums.RouteFlags;
 import com.intel.jndn.management.types.FaceStatus;
 import com.intel.jndn.management.types.RibEntry;
@@ -351,7 +352,7 @@
       NfdcHelper nfdcHelper = new NfdcHelper();
       List<FaceStatus> faceStatusList = null;
       try {
-        faceStatusList = nfdcHelper.faceList();
+        faceStatusList = nfdcHelper.faceList(getActivity().getApplicationContext());
       } catch (Exception e) {
         returnException = e;
       }
@@ -467,20 +468,11 @@
         for (int routeFaceId : m_routeFaceList) {
           nfdcHelper.ribUnregisterPrefix(m_prefix, routeFaceId);
         }
-
         nfdcHelper.shutdown();
         return "OK";
-      }
-      catch (FaceUri.CanonizeError e) {
-        return "Error Destroying dace (" + e.getMessage() + ")";
-      }
-      catch (FaceUri.Error e) {
-        return "Error destroying face (" + e.getMessage() + ")";
-      }
-      catch (Exception e) {
-        return "Error communicating with NFD (" + e.getMessage() + ")";
-      }
-      finally {
+      } catch (ManagementException e) {
+        return "Error removing face: " + e.toString();
+      } finally {
         nfdcHelper.shutdown();
       }
     }
diff --git a/app/src/main/java/net/named_data/nfd/RouteListFragment.java b/app/src/main/java/net/named_data/nfd/RouteListFragment.java
index 69854a7..2201381 100644
--- a/app/src/main/java/net/named_data/nfd/RouteListFragment.java
+++ b/app/src/main/java/net/named_data/nfd/RouteListFragment.java
@@ -1,25 +1,24 @@
 /* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015 Regents of the University of California
- *
+ * Copyright (c) 2015-2016 Regents of the University of California
+ * <p>
  * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
  * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
+ * <p>
  * 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.
- *
+ * <p>
  * 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.
- *
+ * <p>
  * 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.app.Activity;
 import android.content.Context;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -27,6 +26,7 @@
 import android.support.v4.app.ListFragment;
 import android.text.TextUtils;
 import android.util.Pair;
+import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -43,6 +43,8 @@
 import android.app.AlertDialog;
 import android.content.DialogInterface;
 
+import com.intel.jndn.management.ManagementException;
+import com.intel.jndn.management.types.FaceStatus;
 import com.intel.jndn.management.types.RibEntry;
 import com.intel.jndn.management.types.Route;
 
@@ -50,6 +52,7 @@
 import net.named_data.jndn_xx.util.FaceUri;
 import net.named_data.nfd.utils.G;
 import net.named_data.nfd.utils.NfdcHelper;
+import net.named_data.nfd.utils.PermanentFaceUriAndRouteManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -91,8 +94,7 @@
   }
 
   @Override
-  public void onViewCreated(View view, Bundle savedInstanceState)
-  {
+  public void onViewCreated(View view, Bundle savedInstanceState) {
     super.onViewCreated(view, savedInstanceState);
     View v = getLayoutInflater(savedInstanceState).inflate(R.layout.fragment_route_list_list_header, null);
     getListView().addHeaderView(v, null, false);
@@ -101,29 +103,30 @@
     m_routeListInfoUnavailableView = v.findViewById(R.id.route_list_info_unavailable);
 
     // Get progress bar spinner view
-    m_reloadingListProgressBar = (ProgressBar)v.findViewById(R.id.route_list_reloading_list_progress_bar);
+    m_reloadingListProgressBar = (ProgressBar) v.findViewById(R.id.route_list_reloading_list_progress_bar);
 
     getListView().setLongClickable(true);
     getListView().setOnItemLongClickListener(new OnItemLongClickListener() {
       public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
-        final RibEntry entry  = (RibEntry)parent.getItemAtPosition(position);;
+        final RibEntry entry = (RibEntry) parent.getItemAtPosition(position);
+        ;
         new AlertDialog.Builder(v.getContext())
-          .setIcon(android.R.drawable.ic_dialog_alert)
-          .setTitle("Deleting route")
-          .setMessage("Are you sure you want to delete " + entry.getName().toUri() + "?")
-          .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-              List<Integer> faceList = new ArrayList<>();
-              for (Route r : entry.getRoutes()) {
-                faceList.add(r.getFaceId());
+            .setIcon(android.R.drawable.ic_dialog_alert)
+            .setTitle("Deleting route")
+            .setMessage("Are you sure you want to delete " + entry.getName().toUri() + "?")
+            .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+              @Override
+              public void onClick(DialogInterface dialog, int which) {
+                List<Integer> faceList = new ArrayList<>();
+                for (Route r : entry.getRoutes()) {
+                  faceList.add(r.getFaceId());
+                }
+                removeRoute(entry.getName(), faceList);
+                Toast.makeText(getActivity(), "Route Deleted", Toast.LENGTH_LONG).show();
               }
-              removeRoute(entry.getName(), faceList);
-              Toast.makeText(getActivity(), "Route Deleted", Toast.LENGTH_LONG).show();
-            }
-          })
-          .setNegativeButton("No", null)
-          .show();
+            })
+            .setNegativeButton("No", null)
+            .show();
 
 
         return true;
@@ -132,8 +135,7 @@
   }
 
   @Override
-  public void onActivityCreated(@Nullable Bundle savedInstanceState)
-  {
+  public void onActivityCreated(@Nullable Bundle savedInstanceState) {
     super.onActivityCreated(savedInstanceState);
     if (m_routeListAdapter == null) {
       m_routeListAdapter = new RouteListAdapter(getActivity());
@@ -144,15 +146,13 @@
   }
 
   @Override
-  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
-  {
+  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
     super.onCreateOptionsMenu(menu, inflater);
     inflater.inflate(R.menu.menu_route_list, menu);
   }
 
   @Override
-  public boolean onOptionsItemSelected(MenuItem item)
-  {
+  public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
       case R.id.route_list_refresh:
         retrieveRouteList();
@@ -186,36 +186,86 @@
   }
 
   @Override
-  public void onDestroyView()
-  {
+  public void onDestroyView() {
     super.onDestroyView();
     setListAdapter(null);
   }
 
   @Override
-  public void onListItemClick(ListView l, View v, int position, long id)
-  {
+  public void onListItemClick(ListView l, View v, int position, long id) {
     if (m_callbacks != null) {
-      RibEntry ribEntry = (RibEntry)l.getAdapter().getItem(position);
+      RibEntry ribEntry = (RibEntry) l.getAdapter().getItem(position);
       m_callbacks.onRouteItemSelected(ribEntry);
     }
   }
 
   @Override
   public void
-  createRoute(Name prefix, String faceUri)
-  {
-    m_routeCreateAsyncTask = new RouteCreateAsyncTask(prefix, faceUri);
+  createRoute(Name prefix, String faceUri, boolean isPermanent) {
+    m_routeCreateAsyncTask = new RouteCreateAsyncTask(prefix, faceUri, isPermanent);
     m_routeCreateAsyncTask.execute();
   }
 
   public void
-  removeRoute(Name prefix, List<Integer> faceIds)
-  {
+  removeRoute(Name prefix, List<Integer> faceIds) {
     m_routeRemoveAsyncTask = new RouteRemoveAsyncTask(prefix, faceIds);
     m_routeRemoveAsyncTask.execute();
   }
 
+  /**
+   * Synchronously remove route
+   */
+  public static void
+  removeOneRouteSync(Context applicationContext,
+                     NfdcHelper nfdcHelper,
+                     FaceStatus face,
+                     Name prefix) throws ManagementException {
+    nfdcHelper.ribUnregisterPrefix(prefix, face.getFaceId());
+    PermanentFaceUriAndRouteManager.deletePermanentRoute(
+        applicationContext,
+        prefix.toString(),
+        face.getRemoteUri()
+    );
+  }
+
+  public static void
+  removeRouteSyncs(Context applicationContext,
+                   Name prefix,
+                   Iterable<Integer> faceIds) throws Exception {
+    NfdcHelper nfdcHelper = new NfdcHelper();
+    SparseArray<FaceStatus> faceSparseArray = nfdcHelper.faceListAsSparseArray(applicationContext);
+    try {
+      for (int faceId : faceIds) {
+        removeOneRouteSync(
+            applicationContext,
+            nfdcHelper,
+            faceSparseArray.get(faceId),
+            prefix);
+      }
+    } finally {
+      nfdcHelper.shutdown();
+    }
+  }
+
+  public static void
+  removeRouteSyncs(Context applicationContext,
+                   Integer faceId,
+                   Iterable<Name> prefixes) throws ManagementException {
+    NfdcHelper nfdcHelper = new NfdcHelper();
+    SparseArray<FaceStatus> faceSparseArray = nfdcHelper.faceListAsSparseArray(applicationContext);
+    try {
+      for (Name prefix : prefixes) {
+        removeOneRouteSync(
+            applicationContext,
+            nfdcHelper,
+            faceSparseArray.get(faceId),
+            prefix);
+      }
+    } finally {
+      nfdcHelper.shutdown();
+    }
+  }
+
   /////////////////////////////////////////////////////////////////////////
 
   /**
@@ -225,20 +275,22 @@
    *
    * @param list Update ListView with the given List&lt;RibEntry&gt;
    */
-  private void updateRouteList(List<RibEntry> list) {
+  private void
+  updateRouteList(List<RibEntry> list) {
     if (list == null) {
       m_routeListInfoUnavailableView.setVisibility(View.VISIBLE);
       return;
     }
 
-    ((RouteListAdapter)getListAdapter()).updateList(list);
+    ((RouteListAdapter) getListAdapter()).updateList(list);
   }
 
   /**
    * Convenience method that starts the AsyncTask that retrieves the
    * list of available routes.
    */
-  private void retrieveRouteList() {
+  private void
+  retrieveRouteList() {
     // Update UI
     m_routeListInfoUnavailableView.setVisibility(View.GONE);
 
@@ -250,7 +302,8 @@
   /**
    * Create a new AsynTask for route list information retrieval.
    */
-  private void startRouteListInfoRetrievalTask() {
+  private void
+  startRouteListInfoRetrievalTask() {
     m_routeListAsyncTask = new RouteListAsyncTask();
     m_routeListAsyncTask.execute();
   }
@@ -258,7 +311,8 @@
   /**
    * Stops a previously started AsyncTask.
    */
-  private void stopRouteListInfoRetrievalTask() {
+  private void
+  stopRouteListInfoRetrievalTask() {
     if (m_routeListAsyncTask != null) {
       m_routeListAsyncTask.cancel(false);
       m_routeListAsyncTask = null;
@@ -269,34 +323,31 @@
 
   private static class RouteListAdapter extends BaseAdapter {
 
-    public RouteListAdapter(Context context) {
+    RouteListAdapter(Context context) {
       m_layoutInflater = LayoutInflater.from(context);
     }
 
-    public void
+    void
     updateList(List<RibEntry> ribEntries) {
       m_ribEntries = ribEntries;
       notifyDataSetChanged();
     }
 
     @Override
-    public int getCount()
-    {
+    public int getCount() {
       return (m_ribEntries == null) ? 0 : m_ribEntries.size();
     }
 
     @Override
     public RibEntry
-    getItem(int i)
-    {
+    getItem(int i) {
       assert m_ribEntries != null;
       return m_ribEntries.get(i);
     }
 
     @Override
     public long
-    getItemId(int i)
-    {
+    getItemId(int i) {
       return i;
     }
 
@@ -357,8 +408,7 @@
       List<RibEntry> routes = null;
       try {
         routes = nfdcHelper.ribList();
-      }
-      catch (Exception e) {
+      } catch (Exception e) {
         returnException = e;
       }
       nfdcHelper.shutdown();
@@ -378,8 +428,8 @@
 
       if (result.second != null) {
         Toast.makeText(getActivity(),
-                       "Error communicating with NFD (" + result.second.getMessage() + ")",
-                       Toast.LENGTH_LONG).show();
+            "Error communicating with NFD (" + result.second.getMessage() + ")",
+            Toast.LENGTH_LONG).show();
       }
 
       updateRouteList(result.first);
@@ -387,50 +437,51 @@
   }
 
   private class RouteCreateAsyncTask extends AsyncTask<Void, Void, String> {
-    public
-    RouteCreateAsyncTask(Name prefix, String faceUri)
-    {
+    RouteCreateAsyncTask(Name prefix, String faceUri, boolean isPermanent) {
       m_prefix = prefix;
       m_faceUri = faceUri;
+      m_isPermanent = isPermanent;
     }
 
     @Override
     protected String
-    doInBackground(Void... params)
-    {
+    doInBackground(Void... params) {
       NfdcHelper nfdcHelper = new NfdcHelper();
       try {
         int faceId = nfdcHelper.faceCreate(m_faceUri);
         nfdcHelper.ribRegisterPrefix(new Name(m_prefix), faceId, 10, true, false);
+        if (m_isPermanent) {
+          Context context = getActivity().getApplicationContext();
+          PermanentFaceUriAndRouteManager
+              .addPermanentRoute(
+                  context,
+                  m_prefix.toUri(),
+                  NfdcHelper.formatFaceUri(m_faceUri)
+              );
+        }
         nfdcHelper.shutdown();
         return "OK";
-      }
-      catch (FaceUri.CanonizeError e) {
+      } catch (FaceUri.CanonizeError e) {
         return "Error creating face (" + e.getMessage() + ")";
-      }
-      catch (FaceUri.Error e) {
+      } catch (FaceUri.Error e) {
         return "Error creating face (" + e.getMessage() + ")";
-      }
-      catch (Exception e) {
+      } catch (Exception e) {
         return "Error communicating with NFD (" + e.getMessage() + ")";
-      }
-      finally {
+      } finally {
         nfdcHelper.shutdown();
       }
     }
 
     @Override
     protected void
-    onPreExecute()
-    {
+    onPreExecute() {
       // Display progress bar
       m_reloadingListProgressBar.setVisibility(View.VISIBLE);
     }
 
     @Override
     protected void
-    onPostExecute(String status)
-    {
+    onPostExecute(String status) {
       // Display progress bar
       m_reloadingListProgressBar.setVisibility(View.VISIBLE);
       Toast.makeText(getActivity(), status, Toast.LENGTH_LONG).show();
@@ -440,8 +491,7 @@
 
     @Override
     protected void
-    onCancelled()
-    {
+    onCancelled() {
       // Remove progress bar
       m_reloadingListProgressBar.setVisibility(View.GONE);
     }
@@ -450,55 +500,41 @@
 
     private Name m_prefix;
     private String m_faceUri;
+    private boolean m_isPermanent;
   }
 
+
   private class RouteRemoveAsyncTask extends AsyncTask<Void, Void, String> {
-    public
-    RouteRemoveAsyncTask(Name prefix, List<Integer> faceIds)
-    {
+    public RouteRemoveAsyncTask(Name prefix, List<Integer> faceIds) {
       m_prefix = prefix;
       m_faceList = faceIds;
     }
 
     @Override
     protected String
-    doInBackground(Void... params)
-    {
-      NfdcHelper nfdcHelper = new NfdcHelper();
+    doInBackground(Void... params) {
       try {
-        for (int faceId : m_faceList) {
-          nfdcHelper.ribUnregisterPrefix(m_prefix, faceId);
-        }
-
-        nfdcHelper.shutdown();
+        removeRouteSyncs(getActivity().getApplicationContext(), m_prefix, m_faceList);
         return "OK";
-      }
-      catch (FaceUri.CanonizeError e) {
-        return "Error Destroying dace (" + e.getMessage() + ")";
-      }
-      catch (FaceUri.Error e) {
+      } catch (FaceUri.CanonizeError e) {
         return "Error destroying face (" + e.getMessage() + ")";
-      }
-      catch (Exception e) {
+      } catch (FaceUri.Error e) {
+        return "Error destroying face (" + e.getMessage() + ")";
+      } catch (Exception e) {
         return "Error communicating with NFD (" + e.getMessage() + ")";
       }
-      finally {
-        nfdcHelper.shutdown();
-      }
     }
 
     @Override
     protected void
-    onPreExecute()
-    {
+    onPreExecute() {
       // Display progress bar
       m_reloadingListProgressBar.setVisibility(View.VISIBLE);
     }
 
     @Override
     protected void
-    onPostExecute(String status)
-    {
+    onPostExecute(String status) {
       // Display progress bar
       m_reloadingListProgressBar.setVisibility(View.VISIBLE);
       Toast.makeText(getActivity(), status, Toast.LENGTH_LONG).show();
@@ -508,8 +544,7 @@
 
     @Override
     protected void
-    onCancelled()
-    {
+    onCancelled() {
       // Remove progress bar
       m_reloadingListProgressBar.setVisibility(View.GONE);
     }
diff --git a/app/src/main/java/net/named_data/nfd/service/NfdService.java b/app/src/main/java/net/named_data/nfd/service/NfdService.java
index 74adec3..55a4f9d 100644
--- a/app/src/main/java/net/named_data/nfd/service/NfdService.java
+++ b/app/src/main/java/net/named_data/nfd/service/NfdService.java
@@ -20,17 +20,21 @@
 package net.named_data.nfd.service;
 
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
 
+import net.named_data.jndn.Name;
 import net.named_data.nfd.utils.G;
+import net.named_data.nfd.utils.NfdcHelper;
+import net.named_data.nfd.utils.PermanentFaceUriAndRouteManager;
 
 import java.util.HashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -86,6 +90,13 @@
   public native static void
   stopNfd();
 
+  /**
+   * Native API for getting NFD status
+   * @return if NFD is running return true; otherwise false.
+   */
+  public native static boolean
+  isNfdRunning();
+
   public native static List<String>
   getNfdLogModules();
 
@@ -101,17 +112,20 @@
   /** Message to indicate that NFD Service is not running */
   public static final int NFD_SERVICE_STOPPED = 4;
 
+  /** debug tag */
+  public static final String TAG = NfdService.class.getName();
+
 
   @Override
   public void onCreate() {
-    G.Log("NFDService::onCreate()");
+    G.Log(TAG, "NFDService::onCreate()");
     m_nfdServiceMessenger = new Messenger(new NfdServiceMessageHandler());
   }
 
   @Override
   public int
   onStartCommand(Intent intent, int flags, int startId) {
-    G.Log("NFDService::onStartCommand()");
+    G.Log(TAG, "NFDService::onStartCommand()");
 
     serviceStartNfd();
 
@@ -171,9 +185,33 @@
       // from a Handler's message through binding with the service.
       startService(new Intent(this, NfdService.class));
 
-      G.Log("serviceStartNfd()");
+      // Restore PermanentFaceUriAndRoute once NFD is running
+      onNfdStart(new Runnable() {
+        @Override
+        public void run() {
+          createPermanentFaceUriAndRoute(getApplicationContext());
+        }
+      });
+
+      G.Log(TAG, "serviceStartNfd()");
     } else {
-      G.Log("serviceStartNfd(): NFD Service already running!");
+      G.Log(TAG, "serviceStartNfd(): NFD Service already running!");
+    }
+  }
+
+  private void onNfdStart(final Runnable task) {
+    final long checkInterval = 1000;
+    if (isNfdRunning()) {
+      G.Log(TAG, "onNfdStart: NFD is running, start executing task.");
+      task.run();
+    } else {
+      G.Log(TAG, "onNfdStart: NFD is not started yet, delay " + String.valueOf(checkInterval) + " ms.");
+      m_handler.postDelayed(new Runnable() {
+        @Override
+        public void run() {
+          onNfdStart(task);
+        }
+      }, checkInterval);
     }
   }
 
@@ -188,12 +226,85 @@
 
       // TODO: Save NFD and NRD in memory data structures.
       stopNfd();
-
+      PermanentFaceUriAndRouteManager.clearFaceIds(getApplicationContext());
       stopSelf();
-      G.Log("serviceStopNfd()");
+      G.Log(TAG, "serviceStopNfd()");
     }
   }
 
+
+  /**
+   * Create all permanent faces in the background
+   */
+  private static class FaceCreateAsyncTask extends AsyncTask<Void, Void, String> {
+    Context context;
+
+    FaceCreateAsyncTask(Context ctx) {
+      this.context = ctx;
+    }
+
+    @Override
+    protected String
+    doInBackground(Void... params) {
+      NfdcHelper nfdcHelper = new NfdcHelper();
+      try {
+        G.Log(TAG, "Try to create permanent face");
+        Set<String> permanentFace = PermanentFaceUriAndRouteManager.getPermanentFaceUris(this.context);
+        G.Log(TAG, "Permanent face list has " + permanentFace.size() + " item(s)");
+        for (String one : permanentFace) {
+          int faceId = nfdcHelper.faceCreate(one);
+          PermanentFaceUriAndRouteManager.addPermanentFaceId(this.context, faceId);
+          G.Log(TAG, "Create permanent face " + one);
+        }
+      } catch (Exception e) {
+        G.Log(TAG, "Error in FaceCreateAsyncTask: " + e.getMessage());
+      } finally {
+        nfdcHelper.shutdown();
+      }
+      return null;
+    }
+  }
+
+  /**
+   * Create all permanent routes in the background
+   */
+  private static class RouteCreateAsyncTask extends AsyncTask<Void, Void, String> {
+    Context context;
+    RouteCreateAsyncTask(Context ctx) {
+      this.context = ctx;
+    }
+
+    @Override
+    protected String
+    doInBackground(Void... params) {
+      NfdcHelper nfdcHelper = new NfdcHelper();
+      try {
+        G.Log(TAG, "Try to create permanent route");
+        Set<String[]> prefixAndFacePairs = PermanentFaceUriAndRouteManager.getPermanentRoutes(this.context);
+        G.Log(TAG, "Permanent face list has " + prefixAndFacePairs.size() + " item(s)");
+        for (String[] prefixAndFaceUri : prefixAndFacePairs) {
+          int faceId = nfdcHelper.faceCreate(prefixAndFaceUri[1]);
+          nfdcHelper.ribRegisterPrefix(new Name(prefixAndFaceUri[0]), faceId, 10, true, false);
+          G.Log(TAG, "Create permanent route" + prefixAndFaceUri[0] + " - " + prefixAndFaceUri[1]);
+        }
+      } catch (Exception e) {
+        G.Log(TAG, "Error in RouteCreateAsyncTask: " + e.getMessage());
+      } finally {
+        nfdcHelper.shutdown();
+      }
+      return null;
+    }
+  }
+
+  /**
+   * create all permanent faces and routes in the background
+   * @param ctx: Application Context
+   */
+  public static void createPermanentFaceUriAndRoute(Context ctx) {
+    new FaceCreateAsyncTask(ctx).execute();
+    new RouteCreateAsyncTask(ctx).execute();
+  }
+
   /**
    * Message handler for the the NFD Service.
    */
@@ -233,4 +344,7 @@
 
   /** Flag that denotes if the NFD has been started */
   private boolean m_isNfdStarted = false;
+
+  /** Handler to deal with timeout behaviors */
+  private Handler m_handler = new Handler();
 }
diff --git a/app/src/main/java/net/named_data/nfd/utils/G.java b/app/src/main/java/net/named_data/nfd/utils/G.java
index 3c05305..f251466 100644
--- a/app/src/main/java/net/named_data/nfd/utils/G.java
+++ b/app/src/main/java/net/named_data/nfd/utils/G.java
@@ -1,18 +1,18 @@
 /* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
  * Copyright (c) 2015 Regents of the University of California
- *
+ * <p>
  * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
  * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
+ * <p>
  * 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.
- *
+ * <p>
  * 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.
- *
+ * <p>
  * 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/>.
  */
diff --git a/app/src/main/java/net/named_data/nfd/utils/NfdcHelper.java b/app/src/main/java/net/named_data/nfd/utils/NfdcHelper.java
index 35854f0..178870c 100644
--- a/app/src/main/java/net/named_data/nfd/utils/NfdcHelper.java
+++ b/app/src/main/java/net/named_data/nfd/utils/NfdcHelper.java
@@ -1,33 +1,39 @@
 /* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015 Regents of the University of California
- *
+ * Copyright (c) 2015-2016 Regents of the University of California
+ * <p>
  * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
  * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
+ * <p>
  * 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.
- *
+ * <p>
  * 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.
- *
+ * <p>
  * 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.utils;
 
+import android.content.Context;
+import android.util.SparseArray;
+
 import com.intel.jndn.management.ManagementException;
 import com.intel.jndn.management.Nfdc;
+import com.intel.jndn.management.enums.FacePersistency;
 import com.intel.jndn.management.types.FaceStatus;
 import com.intel.jndn.management.types.ForwarderStatus;
 import com.intel.jndn.management.types.RibEntry;
+import com.intel.jndn.management.types.Route;
 
 import net.named_data.jndn.ControlParameters;
 import net.named_data.jndn.Face;
 import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Interest;
 import net.named_data.jndn.Name;
 import net.named_data.jndn.security.*;
 import net.named_data.jndn.security.SecurityException;
@@ -37,7 +43,12 @@
 import net.named_data.jndn.security.policy.SelfVerifyPolicyManager;
 import net.named_data.jndn_xx.util.FaceUri;
 
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public class NfdcHelper
 {
@@ -72,7 +83,11 @@
    * Registers name to the given faceId or faceUri
    */
   public void
-  ribRegisterPrefix(Name prefix, int faceId, int cost, boolean isChildInherit, boolean isCapture) throws Exception
+  ribRegisterPrefix(Name prefix,
+                    int faceId,
+                    int cost,
+                    boolean isChildInherit,
+                    boolean isCapture) throws Exception
   {
     ForwardingFlags flags = new ForwardingFlags();
     flags.setChildInherit(isChildInherit);
@@ -89,8 +104,7 @@
    * Unregisters name from the given faceId/faceUri
    */
   public void
-  ribUnregisterPrefix(Name prefix, int faceId) throws Exception
-  {
+  ribUnregisterPrefix(Name prefix, int faceId) throws ManagementException {
     Nfdc.unregister(m_face,
                     new ControlParameters()
                       .setName(prefix)
@@ -101,11 +115,28 @@
    * List all of routes (RIB entries)
    */
   public List<RibEntry>
-  ribList() throws Exception
-  {
+  ribList() throws ManagementException {
     return Nfdc.getRouteList(m_face);
   }
 
+  public SparseArray<Set<Name>>
+  ribAsFaceIdPrefixNameArray() throws ManagementException {
+    List<RibEntry> ribEntryList = ribList();
+    SparseArray<Set<Name>> faceIdPrefixArray = new SparseArray<>();
+
+    for (RibEntry rib: ribEntryList){
+      for (Route route : rib.getRoutes()){
+        Set<Name> prefixes = faceIdPrefixArray.get(route.getFaceId(), null);
+        if (null == prefixes){
+          prefixes = new HashSet<>();
+          faceIdPrefixArray.append(route.getFaceId(), prefixes);
+        }
+        prefixes.add(rib.getName());
+      }
+    }
+    return faceIdPrefixArray;
+  }
+
   /**
    * Creates new face
    * <p>
@@ -114,7 +145,7 @@
   public int
   faceCreate(String faceUri) throws ManagementException, FaceUri.Error, FaceUri.CanonizeError
   {
-    return Nfdc.createFace(m_face, new FaceUri(faceUri).canonize().toString());
+    return Nfdc.createFace(m_face, formatFaceUri(faceUri));
   }
 
   /**
@@ -130,9 +161,42 @@
    * List all faces
    */
   public List<FaceStatus>
-  faceList() throws Exception
+  faceList(Context context) throws ManagementException
   {
-    return Nfdc.getFaceList(m_face);
+    List<FaceStatus> result = Nfdc.getFaceList(m_face);
+    for(FaceStatus one : result) {
+      if(PermanentFaceUriAndRouteManager.isPermanentFace(context, one.getFaceId())) {
+        one.setFacePersistency(FacePersistency.PERMANENT);
+      }
+    }
+    return result;
+  }
+
+  public SparseArray<FaceStatus>
+  faceListAsSparseArray(Context context) throws ManagementException {
+    List<FaceStatus> faceList = faceList(context);
+    SparseArray<FaceStatus> array = new SparseArray<>();
+    for (FaceStatus face : faceList){
+      array.append(face.getFaceId(), face);
+    }
+    return array;
+  }
+
+  public Map<String, FaceStatus>
+  faceListAsFaceUriMap(Context context) throws ManagementException{
+    List<FaceStatus> faceList = faceList(context);
+    Map<String, FaceStatus> map = new HashMap<>();
+    for (FaceStatus face: faceList){
+      map.put(face.getRemoteUri(), face);
+    }
+    return map;
+  }
+
+  /**
+   * format a faceUri
+   */
+  public static String formatFaceUri(String faceUri) throws FaceUri.CanonizeError {
+    return new FaceUri(faceUri).canonize().toString();
   }
 
 //  /**
diff --git a/app/src/main/java/net/named_data/nfd/utils/PermanentFaceUriAndRouteManager.java b/app/src/main/java/net/named_data/nfd/utils/PermanentFaceUriAndRouteManager.java
new file mode 100644
index 0000000..3f2ccdd
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/utils/PermanentFaceUriAndRouteManager.java
@@ -0,0 +1,167 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015-2016 Regents of the University of California
+ * <p>
+ * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
+ * See AUTHORS.md for complete list of NFD Android authors and contributors.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The manager to record and delete permanent faceUris and routes
+ */
+public class PermanentFaceUriAndRouteManager {
+  private static final String TAG = "Permanent Manager";
+  private static final String PREFS_NAME = "permanent";
+  private static final String PERMANENT_FACEURI = "permanentFaceUri";
+  private static final String PERMANENT_ROUTE = "permanentRoute";
+  private static final String PERMANENT_FACEID = "permanentFaceId";
+  private static final String PREFIX_FACEURI_DELIMITER = "\t";
+  // We need to cache permanent face IDs in order to display whether a face is permanent face or not.
+
+  @SuppressWarnings("deprecation")
+  public static void addPermanentFaceId(Context context, int faceId) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    Set<String> permanentFace = setting.getStringSet(PERMANENT_FACEID, new HashSet<String>());
+    Set<String> newPermanentFace = new HashSet<>(permanentFace);
+
+    if (newPermanentFace.add(Integer.toString(faceId))) {
+      SharedPreferences.Editor editor = setting.edit();
+      editor.putStringSet(PERMANENT_FACEID, newPermanentFace);
+      editor.commit();
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  public static void deletePermanentFaceId(Context context, int faceId) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    Set<String> permanentFace = setting.getStringSet(PERMANENT_FACEID, new HashSet<String>());
+    Set<String> newPermanentFace = new HashSet<>(permanentFace);
+
+    if (newPermanentFace.remove(Integer.toString(faceId))) {
+      SharedPreferences.Editor editor = setting.edit();
+      editor.putStringSet(PERMANENT_FACEID, newPermanentFace);
+      editor.commit();
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  static boolean isPermanentFace(Context context, int faceId) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    Set<String> permanentFace = setting.getStringSet(PERMANENT_FACEID, new HashSet<String>());
+
+    return permanentFace.contains(Integer.toString(faceId));
+  }
+
+  @SuppressWarnings("deprecation")
+  public static void clearFaceIds(Context context) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+
+    SharedPreferences.Editor editor = setting.edit();
+    editor.putStringSet(PERMANENT_FACEID, new HashSet<String>());
+    editor.commit();
+  }
+
+  @SuppressWarnings("deprecation")
+  public static Set<String> getPermanentFaceUris(Context context){
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    return setting.getStringSet(PERMANENT_FACEURI, new HashSet<String>());
+  }
+
+  @SuppressWarnings("deprecation")
+  public static Set<String[]> getPermanentRoutes(Context context){
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    Set<String> permanentRoutes = setting.getStringSet(PERMANENT_ROUTE, new HashSet<String>());
+    Set<String[]> prefixAndFacePairs = new HashSet<>();
+    for (String oneRecord : permanentRoutes){
+      String[] prefixAndFaceUri = oneRecord.split(PREFIX_FACEURI_DELIMITER);
+      prefixAndFacePairs.add(prefixAndFaceUri);
+    }
+    return prefixAndFacePairs;
+  }
+
+  @SuppressWarnings("deprecation")
+  public static void addPermanentFaceUri(Context context, String faceUri) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    Set<String> permanentFace = setting.getStringSet(PERMANENT_FACEURI, new HashSet<String>());
+    Set<String> newPermanentFace = new HashSet<>(permanentFace);
+
+    G.Log(TAG, "Try to record permanent face");
+    G.Log(TAG, "Permanent face list has " + permanentFace.size() + " item(s)");
+    if (newPermanentFace.add(faceUri)) {
+      SharedPreferences.Editor editor = setting.edit();
+      editor.putStringSet(PERMANENT_FACEURI, newPermanentFace);
+      editor.commit();
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  public static void addPermanentRoute(Context context, String prefix, String faceUri) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    Set<String> permanentRoute = setting.getStringSet(PERMANENT_ROUTE, new HashSet<String>());
+    Set<String> newPermanentRoute = new HashSet<>(permanentRoute);
+
+    G.Log(TAG, "Try to record permanent route");
+    G.Log(TAG, "Permanent route list has " + permanentRoute.size() + " item(s)");
+    if (newPermanentRoute.add(prefix + PREFIX_FACEURI_DELIMITER + faceUri)) {
+      SharedPreferences.Editor editor = setting.edit();
+      editor.putStringSet(PERMANENT_ROUTE, newPermanentRoute);
+      editor.commit();
+      G.Log(TAG, "Record permanent route " + faceUri);
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  public static void deletePermanentFaceUri(Context context, String faceUri) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    Set<String> permanentFace = setting.getStringSet(PERMANENT_FACEURI, new HashSet<String>());
+    Set<String> newPermanentFace = new HashSet<>(permanentFace);
+
+    G.Log(TAG, "Try to delete permanent face");
+    G.Log(TAG, "Permanent face list has " + permanentFace.size() + " item(s)");
+    if (newPermanentFace.remove(faceUri)) {
+      SharedPreferences.Editor editor = setting.edit();
+      editor.putStringSet(PERMANENT_FACEURI, newPermanentFace);
+      editor.commit();
+      G.Log(TAG, "Delete permanent face " + faceUri);
+    } else {
+      G.Log(TAG, faceUri + " is not a permanent face");
+    }
+  }
+
+  @SuppressWarnings("deprecation")
+  public static void deletePermanentRoute(Context context, String prefix, String faceUri) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    Set<String> permanentRoute = setting.getStringSet(PERMANENT_ROUTE, new HashSet<String>());
+    Set<String> newPermanentRoute = new HashSet<>(permanentRoute);
+
+    G.Log(TAG, "Try to delete permanent route");
+    G.Log(TAG, "Permanent route list has " + permanentRoute.size() + " item(s)");
+    if (newPermanentRoute.remove(prefix + PREFIX_FACEURI_DELIMITER + faceUri)) {
+      SharedPreferences.Editor editor = setting.edit();
+      editor.putStringSet(PERMANENT_ROUTE, newPermanentRoute);
+      editor.commit();
+      G.Log(TAG, "Delete permanent route " + prefix + " " + faceUri);
+    } else {
+      G.Log(TAG, prefix + " " + faceUri + " is not a permanent route");
+    }
+  }
+}
diff --git a/app/src/main/jni/nfd-wrapper.cpp b/app/src/main/jni/nfd-wrapper.cpp
index 90e19ed..a04585e 100644
--- a/app/src/main/jni/nfd-wrapper.cpp
+++ b/app/src/main/jni/nfd-wrapper.cpp
@@ -293,6 +293,12 @@
   }
 }
 
+JNIEXPORT jboolean JNICALL
+Java_net_named_1data_nfd_service_NfdService_isNfdRunning(JNIEnv*, jclass)
+{
+    return nfd::g_runner.get() != nullptr;
+}
+
 JNIEXPORT jobject JNICALL
 Java_net_named_1data_nfd_service_NfdService_getNfdLogModules(JNIEnv* env, jclass)
 {
diff --git a/app/src/main/jni/nfd-wrapper.hpp b/app/src/main/jni/nfd-wrapper.hpp
index fcd598d..69a0ab5 100644
--- a/app/src/main/jni/nfd-wrapper.hpp
+++ b/app/src/main/jni/nfd-wrapper.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015 Regents of the University of California
+ * Copyright (c) 2015-2016 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.
@@ -45,6 +45,14 @@
 
 /*
  * Class:     net_named_data_nfd_service_NfdService
+ * Method:    isNfdRunning
+ * Signature: ()L/java/lang/Boolean;
+ */
+JNIEXPORT jboolean JNICALL
+Java_net_named_1data_nfd_service_NfdService_isNfdRunning(JNIEnv*, jclass);
+
+/*
+ * Class:     net_named_data_nfd_service_NfdService
  * Method:    getNfdLogModules
  * Signature: ()Ljava/util/List;
  */
diff --git a/app/src/main/res/layout/dialog_create_face.xml b/app/src/main/res/layout/dialog_create_face.xml
index 02d40a6..516c25a 100644
--- a/app/src/main/res/layout/dialog_create_face.xml
+++ b/app/src/main/res/layout/dialog_create_face.xml
@@ -9,7 +9,7 @@
         android:layout_height="wrap_content"
         style="@style/default_dialog_margin"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:text="Enter FaceUri for the remote NDN daemon"
+        android:text="@string/dialog_add_face_edit_title"
         android:layout_gravity="center_horizontal"/>
 
     <EditText
@@ -17,7 +17,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         style="@style/default_dialog_margin"
-        android:hint="Face URI"
+        android:hint="@string/dialog_add_route_edit_face_uri_hint"
         android:inputType="text"
         android:focusable="true"
         android:focusableInTouchMode="true"
@@ -25,4 +25,12 @@
         <requestFocus />
      </EditText>
 
+    <CheckBox
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/dialog_add_route_or_face_as_permanent"
+        android:id="@+id/permanent"
+        android:layout_gravity="center_horizontal"
+        android:checked="false" />
+
 </LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_create_route.xml b/app/src/main/res/layout/dialog_create_route.xml
index 67c7677..5d8630d 100644
--- a/app/src/main/res/layout/dialog_create_route.xml
+++ b/app/src/main/res/layout/dialog_create_route.xml
@@ -9,7 +9,7 @@
         android:layout_height="wrap_content"
         style="@style/default_dialog_margin"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:text="Enter prefix and FaceUri to register with NDN daemon"
+        android:text="@string/dialog_add_route_edit_title"
         android:layout_gravity="center_horizontal"/>
 
     <EditText
@@ -17,7 +17,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         style="@style/default_dialog_margin"
-        android:hint="Prefix"
+        android:hint="@string/dialog_add_route_edit_prefix_hint"
         android:inputType="text"
         android:focusable="true"
         android:focusableInTouchMode="true"
@@ -30,11 +30,18 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         style="@style/default_dialog_margin"
-        android:hint="Face URI"
+        android:hint="@string/dialog_add_route_edit_face_uri_hint"
         android:inputType="text"
         android:focusable="true"
         android:focusableInTouchMode="true"
         android:selectAllOnFocus="true">
      </EditText>
 
+    <CheckBox
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/dialog_add_route_or_face_as_permanent"
+        android:id="@+id/permanent"
+        android:layout_gravity="center_horizontal" />
+
 </LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d25909a..ff141d1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -31,10 +31,15 @@
     <string name="route_list_list_of_routes_title">List of Routes</string>
     <string name="route_list_refresh_route_list">Refresh Route List</string>
     <string name="route_list_add_route">Add Route</string>
+    <string name="dialog_add_route_edit_title">Enter prefix and FaceUri to register with NDN daemon</string>
+    <string name="dialog_add_route_edit_prefix_hint">Prefix</string>
+    <string name="dialog_add_route_edit_face_uri_hint">Face URI</string>
+    <string name="dialog_add_route_or_face_as_permanent">Keep it permanent</string>
     <string name="face_list_actions_title">Face List Actions</string>
     <string name="face_list_refresh_face_list">Refresh Face List</string>
-    <string name="face_list_add_face">Add Face</string>
     <string name="face_list_list_of_faces_title">List of Faces</string>
+    <string name="face_list_add_face">Add Face</string>
+    <string name="dialog_add_face_edit_title">Enter FaceUri for the remote NDN daemon</string>
     <string name="fragment_face_details_title">Face Details</string>
     <string name="menu_item_delete_face_item">Delete</string>
     <string name="expire_never">Never</string>