Add support for NDN-FCH hub discovery

Change-Id: Iff39ef6667e7b6e69be7ae8205cc387015f44631
Refs: #4028
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 4a605d1..022ec32 100644
--- a/app/src/main/java/net/named_data/nfd/FaceListFragment.java
+++ b/app/src/main/java/net/named_data/nfd/FaceListFragment.java
@@ -1,6 +1,6 @@
 /* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015 Regents of the University of California
+ * Copyright (c) 2015-2017 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.
@@ -50,7 +50,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 net.named_data.nfd.utils.SharedPreferencesManager;
 
 import java.util.HashSet;
 import java.util.List;
@@ -436,11 +436,11 @@
             if (null != one){
               removeRoutesOfFace(nfdcHelper, faceId);
               // TODO: what if face was saved but is not in the face list? Is it possible?
-              PermanentFaceUriAndRouteManager.deletePermanentFaceUri(
+              SharedPreferencesManager.deletePermanentFaceUri(
                   context,
                   one.getRemoteUri()
               );
-              PermanentFaceUriAndRouteManager.deletePermanentFaceId(context, faceId);
+              SharedPreferencesManager.deletePermanentFaceId(context, faceId);
               nfdcHelper.faceDestroy(faceId);
             }
           }
@@ -506,11 +506,11 @@
         Context context = getActivity().getApplicationContext();
         int faceId = nfdcHelper.faceCreate(m_faceUri);
         if (m_isPermanent) {
-          PermanentFaceUriAndRouteManager.addPermanentFaceUri(
+          SharedPreferencesManager.addPermanentFaceUri(
                                              context,
                                              NfdcHelper.formatFaceUri(m_faceUri)
                                          );
-          PermanentFaceUriAndRouteManager.addPermanentFaceId(context, faceId);
+          SharedPreferencesManager.addPermanentFaceId(context, faceId);
         }
         nfdcHelper.shutdown();
         return "OK. Face id: " + String.valueOf(faceId);
diff --git a/app/src/main/java/net/named_data/nfd/MainFragment.java b/app/src/main/java/net/named_data/nfd/MainFragment.java
index 72df064..f7a6f2e 100644
--- a/app/src/main/java/net/named_data/nfd/MainFragment.java
+++ b/app/src/main/java/net/named_data/nfd/MainFragment.java
@@ -41,18 +41,34 @@
 import android.widget.CompoundButton;
 import android.widget.Switch;
 import android.widget.TextView;
+import android.widget.Toast;
 
+import com.android.volley.Request;
+import com.android.volley.RequestQueue;
+import com.android.volley.Response;
+import com.android.volley.VolleyError;
+import com.android.volley.toolbox.StringRequest;
+import com.android.volley.toolbox.Volley;
+import com.intel.jndn.management.ManagementException;
 import com.intel.jndn.management.types.ForwarderStatus;
+import com.intel.jndn.management.types.RibEntry;
 
+import net.named_data.jndn.Name;
 import net.named_data.nfd.service.NfdService;
 import net.named_data.nfd.utils.G;
 import net.named_data.nfd.utils.NfdcHelper;
+import net.named_data.nfd.utils.SharedPreferencesManager;
 import net.named_data.nfd.wifidirect.utils.NDNController;
 
 import org.joda.time.Period;
 import org.joda.time.format.PeriodFormat;
 
+import java.util.List;
+
 public class MainFragment extends Fragment {
+  public static final String URI_UDP_PREFIX = "udp://";
+  public static final String PREFIX_NDN = "/";
+  public static final String PREFIX_LOCALHOP_NFD = "/localhop/nfd";
 
   public static MainFragment newInstance() {
     // Create fragment arguments here (if necessary)
@@ -88,6 +104,24 @@
       }
     });
 
+    m_connectNearestHubSwitch = (Switch) v.findViewById(R.id.connect_nearest_hub_switch);
+    if (SharedPreferencesManager.getConnectNearestHubAutomatically(getActivity().getApplicationContext())) {
+      m_connectNearestHubSwitch.setChecked(true);
+    }
+    m_connectNearestHubSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+      @Override
+      public void onCheckedChanged(CompoundButton compoundButton, boolean isOn) {
+        SharedPreferencesManager.
+          setConnectNearestHubAutomatically(getActivity().getApplicationContext(), isOn);
+        if (isOn) {
+          // when nfd service is running, connect NDN hub, otherwise, do nothing
+          if(m_sharedPreferences.getBoolean(PREF_NFD_SERVICE_STATUS, true)) {
+            connectNearestHub();
+          }
+        }
+      }
+    });
+
     m_nfdStatusView = (ViewGroup) v.findViewById(R.id.status_view);
     m_nfdStatusView.setVisibility(View.GONE);
     m_versionView = (TextView) v.findViewById(R.id.version);
@@ -107,6 +141,14 @@
     return v;
   }
 
+  /**
+   * when the user clicks "connect to the nearest hub automatically", create face and register prefix
+   */
+  private void connectNearestHub() {
+    m_connectNearestHubAsyncTask = new ConnectNearestHubAsyncTask();
+    m_connectNearestHubAsyncTask.execute();
+  }
+
   @Override
   public void onActivityCreated(@Nullable Bundle savedInstanceState) {
     super.onActivityCreated(savedInstanceState);
@@ -360,6 +402,91 @@
     }
   }
 
+
+  private class ConnectNearestHubAsyncTask extends AsyncTask<Void, Void, String> {
+
+    @Override
+    protected String
+    doInBackground(Void... params) {
+      //check whether two prefixes exist or not
+      boolean prefix_ndn_exist = false;
+      boolean prefix_localhop_nfd_exist = false;
+      NfdcHelper nfdcHelper = new NfdcHelper();
+      try {
+        List<RibEntry> ribEntries = nfdcHelper.ribList();
+        for (RibEntry one : ribEntries) {
+          if (one.getName().toUri().equals(PREFIX_NDN)) {
+            prefix_ndn_exist = true;
+          }
+
+          if (one.getName().toUri().equals(PREFIX_LOCALHOP_NFD)) {
+            prefix_localhop_nfd_exist = true;
+          }
+        }
+      } catch (ManagementException e) {
+        G.Log("Error fetching RIB list from NFD (" + e.getMessage() + ")");
+      } finally {
+        nfdcHelper.shutdown();
+      }
+      if (prefix_ndn_exist && prefix_localhop_nfd_exist)
+        return "";
+      final boolean prefix_ndn_exist_inner = prefix_ndn_exist;
+      final boolean prefix_localhop_nfd_exist_inner = prefix_localhop_nfd_exist;
+      //register prefixes if they don't exist
+      RequestQueue queue = Volley.newRequestQueue(getContext());
+      StringRequest stringRequest = new StringRequest(Request.Method.GET,
+        getResources().getString(R.string.ndn_fch_website),
+        new Response.Listener<String>() {
+          @Override
+          public void onResponse(String response) {
+            if (!prefix_ndn_exist_inner)
+              new RouteCreateToConnectNearestHubAsyncTask(
+                new Name(PREFIX_NDN), URI_UDP_PREFIX + response).execute();
+            if (!prefix_localhop_nfd_exist_inner)
+              new RouteCreateToConnectNearestHubAsyncTask(
+                new Name(PREFIX_LOCALHOP_NFD), URI_UDP_PREFIX + response).execute();
+          }
+        },
+        new Response.ErrorListener() {
+          @Override
+          public void onErrorResponse(VolleyError error) {
+            String toastString = getResources().getString(R.string.fragment_route_list_toast_cannot_connect_hub);
+            Toast.makeText(getActivity(), toastString, Toast.LENGTH_LONG).show();
+          }
+        });
+      // Add the request to the RequestQueue.
+      queue.add(stringRequest);
+      return "";
+    }
+  }
+
+  private class RouteCreateToConnectNearestHubAsyncTask extends AsyncTask<Void, Void, String> {
+    RouteCreateToConnectNearestHubAsyncTask(Name prefix, String faceUri) {
+      m_prefix = prefix;
+      m_faceUri = faceUri;
+    }
+
+    @Override
+    protected String
+    doInBackground(Void... params) {
+      NfdcHelper nfdcHelper = new NfdcHelper();
+      try {
+        G.Log("Try to create route to connect the nearest hub");
+        int faceId = nfdcHelper.faceCreate(m_faceUri);
+        nfdcHelper.ribRegisterPrefix(m_prefix, faceId, 10, true, false);
+        G.Log("Create permanent route" + m_prefix + " - " + m_faceUri);
+      } catch (Exception e) {
+        G.Log("Error in RouteCreateToConnectNearestHubAsyncTask: " + e.getMessage());
+      } finally {
+        nfdcHelper.shutdown();
+      }
+      return null;
+    }
+
+    private Name m_prefix;
+    private String m_faceUri;
+  }
+
   //////////////////////////////////////////////////////////////////////////////
 
   /**
@@ -368,10 +495,17 @@
   private Switch m_nfdStartStopSwitch;
 
   /**
+   * Button that starts and stops the auto configuration
+   */
+  private Switch m_connectNearestHubSwitch;
+
+  /**
    * Flag that marks that application is connected to the NfdService
    */
   private boolean m_isNfdServiceConnected = false;
 
+  private ConnectNearestHubAsyncTask m_connectNearestHubAsyncTask;
+
   /**
    * Client Message Handler
    */
@@ -412,4 +546,6 @@
   private SharedPreferences m_sharedPreferences;
 
   private static final String PREF_NFD_SERVICE_STATUS = "NFD_SERVICE_STATUS";
+
+  private static final String CONNECT_NEAREST_HUB_STATUS = "CONNECT_NEAREST_HUB_STATUS";
 }
\ No newline at end of file
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 2201381..275dcde 100644
--- a/app/src/main/java/net/named_data/nfd/RouteListFragment.java
+++ b/app/src/main/java/net/named_data/nfd/RouteListFragment.java
@@ -1,6 +1,6 @@
 /* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015-2016 Regents of the University of California
+ * Copyright (c) 2015-2017 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.
@@ -19,7 +19,9 @@
 
 package net.named_data.nfd;
 
+import android.app.AlertDialog;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
@@ -33,15 +35,13 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemLongClickListener;
 import android.widget.BaseAdapter;
 import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 import android.widget.Toast;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.AdapterView;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
 
 import com.intel.jndn.management.ManagementException;
 import com.intel.jndn.management.types.FaceStatus;
@@ -52,7 +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 net.named_data.nfd.utils.SharedPreferencesManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -75,11 +75,10 @@
   }
 
   @Override
-  public void onAttach(Context context)
-  {
+  public void onAttach(Context context) {
     super.onAttach(context);
     try {
-      m_callbacks = (Callbacks)context;
+      m_callbacks = (Callbacks) context;
     } catch (Exception e) {
       G.Log("Hosting activity must implement this fragment's callbacks: " + e);
     }
@@ -111,22 +110,21 @@
         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());
-                }
-                removeRoute(entry.getName(), faceList);
-                Toast.makeText(getActivity(), "Route Deleted", Toast.LENGTH_LONG).show();
+          .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());
               }
-            })
-            .setNegativeButton("No", null)
-            .show();
+              removeRoute(entry.getName(), faceList);
+            }
+          })
+          .setNegativeButton("No", null)
+          .show();
 
 
         return true;
@@ -221,10 +219,10 @@
                      FaceStatus face,
                      Name prefix) throws ManagementException {
     nfdcHelper.ribUnregisterPrefix(prefix, face.getFaceId());
-    PermanentFaceUriAndRouteManager.deletePermanentRoute(
-        applicationContext,
-        prefix.toString(),
-        face.getRemoteUri()
+    SharedPreferencesManager.deletePermanentRoute(
+      applicationContext,
+      prefix.toString(),
+      face.getRemoteUri()
     );
   }
 
@@ -237,10 +235,10 @@
     try {
       for (int faceId : faceIds) {
         removeOneRouteSync(
-            applicationContext,
-            nfdcHelper,
-            faceSparseArray.get(faceId),
-            prefix);
+          applicationContext,
+          nfdcHelper,
+          faceSparseArray.get(faceId),
+          prefix);
       }
     } finally {
       nfdcHelper.shutdown();
@@ -256,10 +254,10 @@
     try {
       for (Name prefix : prefixes) {
         removeOneRouteSync(
-            applicationContext,
-            nfdcHelper,
-            faceSparseArray.get(faceId),
-            prefix);
+          applicationContext,
+          nfdcHelper,
+          faceSparseArray.get(faceId),
+          prefix);
       }
     } finally {
       nfdcHelper.shutdown();
@@ -270,7 +268,7 @@
 
   /**
    * Updates the underlying adapter with the given list of RibEntry.
-   *
+   * <p>
    * Note: This method should only be called from the UI thread.
    *
    * @param list Update ListView with the given List&lt;RibEntry&gt;
@@ -428,8 +426,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);
@@ -452,12 +450,12 @@
         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)
-              );
+          SharedPreferencesManager
+            .addPermanentRoute(
+              context,
+              m_prefix.toUri(),
+              NfdcHelper.formatFaceUri(m_faceUri)
+            );
         }
         nfdcHelper.shutdown();
         return "OK";
@@ -557,19 +555,29 @@
 
   /////////////////////////////////////////////////////////////////////////////
 
-  /** Callback handler of the hosting activity */
+  /**
+   * Callback handler of the hosting activity
+   */
   private Callbacks m_callbacks;
 
-  /** Reference to the most recent AsyncTask that was created for listing routes */
+  /**
+   * Reference to the most recent AsyncTask that was created for listing routes
+   */
   private RouteListAsyncTask m_routeListAsyncTask;
 
-  /** Reference to the view to be displayed when no information is available */
+  /**
+   * Reference to the view to be displayed when no information is available
+   */
   private View m_routeListInfoUnavailableView;
 
-  /** Progress bar spinner to display to user when destroying faces */
+  /**
+   * Progress bar spinner to display to user when destroying faces
+   */
   private ProgressBar m_reloadingListProgressBar;
 
-  /** Reference to the most recent AsyncTask that was created for creating a route */
+  /**
+   * Reference to the most recent AsyncTask that was created for creating a route
+   */
   private RouteCreateAsyncTask m_routeCreateAsyncTask;
   private RouteRemoveAsyncTask m_routeRemoveAsyncTask;
 
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 1b2d4e4..79d18ca 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
@@ -1,18 +1,18 @@
 /* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
  * Copyright (c) 2015-2017 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/>.
  */
@@ -29,10 +29,21 @@
 import android.os.Messenger;
 import android.os.RemoteException;
 
+import com.android.volley.Request;
+import com.android.volley.RequestQueue;
+import com.android.volley.Response;
+import com.android.volley.VolleyError;
+import com.android.volley.toolbox.StringRequest;
+import com.android.volley.toolbox.Volley;
+import com.intel.jndn.management.ManagementException;
+import com.intel.jndn.management.types.RibEntry;
+
 import net.named_data.jndn.Name;
+import net.named_data.nfd.MainFragment;
+import net.named_data.nfd.R;
 import net.named_data.nfd.utils.G;
 import net.named_data.nfd.utils.NfdcHelper;
-import net.named_data.nfd.utils.PermanentFaceUriAndRouteManager;
+import net.named_data.nfd.utils.SharedPreferencesManager;
 
 import java.util.HashMap;
 import java.util.List;
@@ -41,11 +52,10 @@
 
 /**
  * NfdService that runs the native NFD.
- *
+ * <p>
  * NfdService runs as an independent process within the Android OS that provides
  * service level features to start and stop the NFD native code through the
  * NFD JNI wrapper.
- *
  */
 public class NfdService extends Service {
   /**
@@ -91,6 +101,7 @@
 
   /**
    * Native API for getting NFD status
+   *
    * @return if NFD is running return true; otherwise false.
    */
   public native static boolean
@@ -99,19 +110,29 @@
   public native static List<String>
   getNfdLogModules();
 
-  /** Message to start NFD Service */
+  /**
+   * Message to start NFD Service
+   */
   public static final int START_NFD_SERVICE = 1;
 
-  /** Message to stop NFD Service */
+  /**
+   * Message to stop NFD Service
+   */
   public static final int STOP_NFD_SERVICE = 2;
 
-  /** Message to indicate that NFD Service is running */
+  /**
+   * Message to indicate that NFD Service is running
+   */
   public static final int NFD_SERVICE_RUNNING = 3;
 
-  /** Message to indicate that NFD Service is not running */
+  /**
+   * Message to indicate that NFD Service is not running
+   */
   public static final int NFD_SERVICE_STOPPED = 4;
 
-  /** debug tag */
+  /**
+   * debug tag
+   */
   public static final String TAG = NfdService.class.getName();
 
 
@@ -128,6 +149,7 @@
 
     serviceStartNfd();
     createPermanentFaceUriAndRoute();
+    connectToNeareastHub();
 
     // Service is restarted when killed.
     // Pending intents delivered; null intent redelivered otherwise.
@@ -169,7 +191,7 @@
       m_isNfdStarted = true;
       HashMap<String, String> params = new HashMap<>();
       params.put("homePath", getFilesDir().getAbsolutePath());
-      Set<Map.Entry<String,String>> e = params.entrySet();
+      Set<Map.Entry<String, String>> e = params.entrySet();
 
       startNfd(params);
 
@@ -207,6 +229,22 @@
     }
   }
 
+  private void connectToNeareastHub() {
+    final long checkInterval = 1000;
+    if (isNfdRunning()) {
+      G.Log(TAG, "connectToNeareastHub: NFD is running, start executing task.");
+      new ConnectNearestHubAsyncTask(getApplicationContext()).execute();
+    } else {
+      G.Log(TAG, "connectToNeareastHub: NFD is not started yet, delay " + String.valueOf(checkInterval) + " ms.");
+      m_handler.postDelayed(new Runnable() {
+        @Override
+        public void run() {
+          connectToNeareastHub();
+        }
+      }, checkInterval);
+    }
+  }
+
   /**
    * Thread safe way of stopping the NFD and updating the
    * started flag.
@@ -218,7 +256,7 @@
 
       // TODO: Save NFD and NRD in memory data structures.
       stopNfd();
-      PermanentFaceUriAndRouteManager.clearFaceIds(getApplicationContext());
+      SharedPreferencesManager.clearFaceIds(getApplicationContext());
       stopSelf();
       G.Log(TAG, "serviceStopNfd()");
     }
@@ -241,11 +279,11 @@
       NfdcHelper nfdcHelper = new NfdcHelper();
       try {
         G.Log(TAG, "Try to create permanent face");
-        Set<String> permanentFace = PermanentFaceUriAndRouteManager.getPermanentFaceUris(this.context);
+        Set<String> permanentFace = SharedPreferencesManager.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);
+          SharedPreferencesManager.addPermanentFaceId(this.context, faceId);
           G.Log(TAG, "Create permanent face " + one);
         }
       } catch (Exception e) {
@@ -262,6 +300,7 @@
    */
   private static class RouteCreateAsyncTask extends AsyncTask<Void, Void, String> {
     Context context;
+
     RouteCreateAsyncTask(Context ctx) {
       this.context = ctx;
     }
@@ -272,7 +311,7 @@
       NfdcHelper nfdcHelper = new NfdcHelper();
       try {
         G.Log(TAG, "Try to create permanent route");
-        Set<String[]> prefixAndFacePairs = PermanentFaceUriAndRouteManager.getPermanentRoutes(this.context);
+        Set<String[]> prefixAndFacePairs = SharedPreferencesManager.getPermanentRoutes(this.context);
         G.Log(TAG, "Permanent face list has " + prefixAndFacePairs.size() + " item(s)");
         for (String[] prefixAndFaceUri : prefixAndFacePairs) {
           int faceId = nfdcHelper.faceCreate(prefixAndFaceUri[1]);
@@ -288,6 +327,100 @@
     }
   }
 
+  private static class RouteCreateToConnectNearestHubAsyncTask extends AsyncTask<Void, Void, String> {
+    RouteCreateToConnectNearestHubAsyncTask(Name prefix, String faceUri) {
+      m_prefix = prefix;
+      m_faceUri = faceUri;
+    }
+
+    @Override
+    protected String
+    doInBackground(Void... params) {
+      NfdcHelper nfdcHelper = new NfdcHelper();
+      try {
+        G.Log(TAG, "Try to create route to connect the nearest hub");
+        int faceId = nfdcHelper.faceCreate(m_faceUri);
+        nfdcHelper.ribRegisterPrefix(m_prefix, faceId, 10, true, false);
+        G.Log(TAG, "Create permanent route" + m_prefix + " - " + m_faceUri);
+      } catch (Exception e) {
+        G.Log(TAG, "Error in RouteCreateToConnectNearestHubAsyncTask: " + e.getMessage());
+      } finally {
+        nfdcHelper.shutdown();
+      }
+      return null;
+    }
+
+    private Name m_prefix;
+    private String m_faceUri;
+  }
+
+  private static class ConnectNearestHubAsyncTask extends AsyncTask<Void, Void, String> {
+    Context context;
+
+    ConnectNearestHubAsyncTask(Context ctx) {
+      this.context = ctx;
+    }
+
+    @Override
+    protected String
+    doInBackground(Void... params) {
+      G.Log(TAG, "Try to connect to the nearest hub");
+      if (SharedPreferencesManager.getConnectNearestHubAutomatically(context)) {
+        NfdcHelper nfdcHelper = new NfdcHelper();
+        try {
+          //check whether two prefixes exist or not
+          boolean prefix_ndn_exist = false;
+          boolean prefix_localhop_nfd_exist = false;
+          List<RibEntry> ribEntries = nfdcHelper.ribList();
+          for (RibEntry one : ribEntries) {
+            if (one.getName().toUri().equals(MainFragment.PREFIX_NDN)) {
+              prefix_ndn_exist = true;
+            }
+
+            if (one.getName().toUri().equals(MainFragment.PREFIX_LOCALHOP_NFD)) {
+              prefix_localhop_nfd_exist = true;
+            }
+          }
+
+          //register prefixes if they don't exist
+          if (!prefix_ndn_exist || !prefix_localhop_nfd_exist) {
+            final boolean prefix_ndn_exist_inner = prefix_ndn_exist;
+            final boolean prefix_localhop_nfd_exist_inner = prefix_localhop_nfd_exist;
+            RequestQueue queue = Volley.newRequestQueue(context);
+            StringRequest stringRequest = new StringRequest(Request.Method.GET,
+              context.getResources().getString(R.string.ndn_fch_website),
+              new Response.Listener<String>() {
+                @Override
+                public void onResponse(String response) {
+                  if (!prefix_ndn_exist_inner) {
+                    new RouteCreateToConnectNearestHubAsyncTask(
+                      new Name(MainFragment.PREFIX_NDN), MainFragment.URI_UDP_PREFIX + response).execute();
+                  }
+                  if (!prefix_localhop_nfd_exist_inner) {
+                    new RouteCreateToConnectNearestHubAsyncTask(
+                      new Name(MainFragment.PREFIX_LOCALHOP_NFD), MainFragment.URI_UDP_PREFIX + response).execute();
+                  }
+                }
+              },
+              new Response.ErrorListener() {
+                @Override
+                public void onErrorResponse(VolleyError error) {
+                  G.Log("cannot connect to the nearest hub");
+                }
+              });
+            // Add the request to the RequestQueue.
+            queue.add(stringRequest);
+          }
+        } catch (ManagementException e) {
+          G.Log(TAG, "Error in ConnectNearestHubAsyncTask: " + e.getMessage());
+        } finally {
+          nfdcHelper.shutdown();
+        }
+      }
+      return null;
+    }
+  }
+
   /**
    * Message handler for the the NFD Service.
    */
@@ -296,19 +429,19 @@
     @Override
     public void handleMessage(Message message) {
       switch (message.what) {
-      case NfdService.START_NFD_SERVICE:
-        serviceStartNfd();
-        replyToClient(message, NfdService.NFD_SERVICE_RUNNING);
-        break;
+        case NfdService.START_NFD_SERVICE:
+          serviceStartNfd();
+          replyToClient(message, NfdService.NFD_SERVICE_RUNNING);
+          break;
 
-      case NfdService.STOP_NFD_SERVICE:
-        serviceStopNfd();
-        replyToClient(message, NfdService.NFD_SERVICE_STOPPED);
-        break;
+        case NfdService.STOP_NFD_SERVICE:
+          serviceStopNfd();
+          replyToClient(message, NfdService.NFD_SERVICE_STOPPED);
+          break;
 
-      default:
-        super.handleMessage(message);
-        break;
+        default:
+          super.handleMessage(message);
+          break;
       }
     }
 
@@ -322,12 +455,18 @@
     }
   }
 
-  /** Messenger to handle messages that are passed to the NfdService */
+  /**
+   * Messenger to handle messages that are passed to the NfdService
+   */
   private Messenger m_nfdServiceMessenger = null;
 
-  /** Flag that denotes if the NFD has been started */
+  /**
+   * Flag that denotes if the NFD has been started
+   */
   private boolean m_isNfdStarted = false;
 
-  /** Handler to deal with timeout behaviors */
+  /**
+   * Handler to deal with timeout behaviors
+   */
   private Handler m_handler = new Handler();
 }
diff --git a/app/src/main/java/net/named_data/nfd/utils/ConnectivityChangeReceiver.java b/app/src/main/java/net/named_data/nfd/utils/ConnectivityChangeReceiver.java
index be9e2ee..bf23eb6 100644
--- a/app/src/main/java/net/named_data/nfd/utils/ConnectivityChangeReceiver.java
+++ b/app/src/main/java/net/named_data/nfd/utils/ConnectivityChangeReceiver.java
@@ -1,6 +1,6 @@
 /* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015-2016 Regents of the University of California
+ * Copyright (c) 2015-2017 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.
@@ -57,7 +57,9 @@
   onChange(Context context, NetworkInfo networkInfo) {
     if (networkInfo.isConnected()) {
       G.Log(TAG, "Network is connected");
-      // (re-)start service, triggering (re-)creation of permanent faces and routes
+      // (re-)start service,
+      // (1)triggering (re-)creation of permanent faces and routes
+      // (2)triggering (re-)connection to nearest hub
       context.startService(new Intent(context, NfdService.class));
     }
   }
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 672003e..1eb35b8 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
@@ -176,7 +176,7 @@
   {
     List<FaceStatus> result = Nfdc.getFaceList(m_face);
     for(FaceStatus one : result) {
-      if(PermanentFaceUriAndRouteManager.isPermanentFace(context, one.getFaceId())) {
+      if(SharedPreferencesManager.isPermanentFace(context, one.getFaceId())) {
         one.setFacePersistency(FacePersistency.PERMANENT);
       }
     }
diff --git a/app/src/main/java/net/named_data/nfd/utils/PermanentFaceUriAndRouteManager.java b/app/src/main/java/net/named_data/nfd/utils/SharedPreferencesManager.java
similarity index 89%
rename from app/src/main/java/net/named_data/nfd/utils/PermanentFaceUriAndRouteManager.java
rename to app/src/main/java/net/named_data/nfd/utils/SharedPreferencesManager.java
index 3f2ccdd..9214e59 100644
--- a/app/src/main/java/net/named_data/nfd/utils/PermanentFaceUriAndRouteManager.java
+++ b/app/src/main/java/net/named_data/nfd/utils/SharedPreferencesManager.java
@@ -1,6 +1,6 @@
 /* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2015-2016 Regents of the University of California
+ * Copyright (c) 2015-2017 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.
@@ -28,12 +28,13 @@
 /**
  * The manager to record and delete permanent faceUris and routes
  */
-public class PermanentFaceUriAndRouteManager {
-  private static final String TAG = "Permanent Manager";
+public class SharedPreferencesManager {
+  private static final String TAG = "SharedPreferencesManager";
   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 CONNECT_NEAREAST_HUB = "connectNeareastHub";
   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.
 
@@ -164,4 +165,16 @@
       G.Log(TAG, prefix + " " + faceUri + " is not a permanent route");
     }
   }
+
+  @SuppressWarnings("deprecation")
+  public static void setConnectNearestHubAutomatically(Context context, boolean isOn) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    setting.edit().putBoolean(CONNECT_NEAREAST_HUB, isOn).commit();
+  }
+
+  @SuppressWarnings("deprecation")
+  public static boolean getConnectNearestHubAutomatically(Context context) {
+    SharedPreferences setting = context.getSharedPreferences(PREFS_NAME, Context.MODE_MULTI_PROCESS);
+    return setting.getBoolean(CONNECT_NEAREAST_HUB, false);
+  }
 }
diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml
index 2fb1d1f..b0d48f1 100644
--- a/app/src/main/res/layout/fragment_main.xml
+++ b/app/src/main/res/layout/fragment_main.xml
@@ -26,6 +26,18 @@
         android:text="@string/checking_on_nfd"
         />
 
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="5dp"/>
+
+    <Switch
+        android:id="@+id/connect_nearest_hub_switch"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text="@string/connect_nearest_hub_automatically"
+        />
+
     <LinearLayout android:id="@+id/status_view"
                   android:layout_width="match_parent"
                   android:layout_height="wrap_content"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5aa76b3..47032af 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -107,6 +107,9 @@
 
     <string name="face_add_dialog_create_face">Create face</string>
     <string name="route_add_dialog_create_route">Create route</string>
+    <string name="connect_nearest_hub_automatically">Connect to the nearest hub automatically</string>
+    <string name="ndn_fch_website">http://ndn-fch.named-data.net/</string>
+    <string name="fragment_route_list_toast_cannot_connect_hub">Cannot connect to the nearest hub</string>
     <string name="fragment_route_details_title">Route Details</string>
     <string name="fragment_route_details_next_hops">List of next hops</string>
     <string name="fragment_route_route_name_title">Route Name</string>