gui: Adding Route list/create operations

Change-Id: I5655b7a0d243eb0c59ad0fc8470a497810ca001d
Refs: #2642
diff --git a/app/build.gradle b/app/build.gradle
index 8214644..674060e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -37,6 +37,10 @@
         androidTest.setRoot('tests')
         androidTest.java.srcDirs = ['tests/src']
     }
+    packagingOptions {
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
 
     splits {
         abi {
@@ -137,4 +141,5 @@
     compile 'com.google.protobuf:protobuf-java:2.6.1'
 
     compile 'joda-time:joda-time:2.7'
+    compile 'commons-lang:commons-lang:2.6'
 }
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f8ebe5b..e28c78b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -32,6 +32,10 @@
             android:name=".FaceListActivity"
             android:label="Faces" />
 
+        <activity
+            android:name=".RouteListActivity"
+            android:label="Routes" />
+
         <service
             android:name=".service.NfdService"
             android:process="net.named_data.nfd.service.NfdService"
diff --git a/app/src/main/java/net/named_data/nfd/MainActivity.java b/app/src/main/java/net/named_data/nfd/MainActivity.java
index 883bef1..c92011f 100644
--- a/app/src/main/java/net/named_data/nfd/MainActivity.java
+++ b/app/src/main/java/net/named_data/nfd/MainActivity.java
@@ -85,6 +85,22 @@
                 return true;
               }
             });
+
+          ///////////////////////////////////////////////////////////////////////////
+          // Routes settings
+          ///////////////////////////////////////////////////////////////////////////
+          addPreferencesFromResource(R.xml.pref_routes);
+
+          findPreference("create_route")
+            .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+              @Override
+              public boolean onPreferenceClick(Preference preference)
+              {
+                DialogFragment dialog = new RouteCreateDialog();
+                dialog.show(getFragmentManager(), "RouteCreateFragment");
+                return true;
+              }
+            });
         }
       })
       .commit();
diff --git a/app/src/main/java/net/named_data/nfd/RouteCreateDialog.java b/app/src/main/java/net/named_data/nfd/RouteCreateDialog.java
new file mode 100644
index 0000000..aef371a
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/RouteCreateDialog.java
@@ -0,0 +1,96 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+import android.widget.EditText;
+
+import net.named_data.jndn.Name;
+import net.named_data.jndn_xx.util.FaceUri;
+import net.named_data.nfd.utils.Nfdc;
+import net.named_data.nfd.utils.NfdcAsyncTask;
+
+public class RouteCreateDialog extends DialogFragment
+{
+  @Override
+  public Dialog
+  onCreateDialog(Bundle savedInstanceState) {
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    LayoutInflater inflater = getActivity().getLayoutInflater();
+    builder
+      .setView(inflater.inflate(R.layout.create_route, null))
+      .setPositiveButton("Create route", new DialogInterface.OnClickListener()
+      {
+        @Override
+        public void onClick(DialogInterface dialog, int id)
+        {
+          EditText prefixBox = (EditText)getDialog().findViewById(R.id.prefix);
+          EditText uriBox = (EditText)getDialog().findViewById(R.id.faceUri);
+          final String prefix = prefixBox.getText().toString();
+          final String uri = uriBox.getText().toString();
+          new NfdcAsyncTask(getActivity(),
+                            new NfdcAsyncTask.Task()
+                            {
+                              public String
+                              runTask() throws Exception
+                              {
+                                try {
+                                  Nfdc nfdc = new Nfdc();
+                                  int faceId = nfdc.faceCreate(m_uri);
+                                  boolean ok = nfdc.ribRegisterPrefix(new Name(prefix), faceId, 10, true, false);
+                                  nfdc.shutdown();
+                                  if (ok) {
+                                    return "OK";
+                                  }
+                                  else {
+                                    return "Failed register prefix";
+                                  }
+                                } catch (FaceUri.CanonizeError e) {
+                                  return "Error creating face (" + e.getMessage() + ")";
+                                } catch (FaceUri.Error e) {
+                                  return "Error creating face (" + e.getMessage() + ")";
+                                }
+                              }
+
+                              ///////////////////////////
+                              private String m_uri = uri;
+                            }).execute();
+        }
+      })
+      .setNegativeButton("Cancel", new DialogInterface.OnClickListener()
+      {
+        public void onClick(DialogInterface dialog, int id)
+        {
+          RouteCreateDialog.this.getDialog().cancel();
+        }
+      })
+    ;
+
+    Dialog faceCreateDialog = builder.create();
+    faceCreateDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+    return faceCreateDialog;
+  }
+}
diff --git a/app/src/main/java/net/named_data/nfd/RouteListActivity.java b/app/src/main/java/net/named_data/nfd/RouteListActivity.java
new file mode 100644
index 0000000..5eca4cd
--- /dev/null
+++ b/app/src/main/java/net/named_data/nfd/RouteListActivity.java
@@ -0,0 +1,192 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.app.Activity;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.ListFragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import com.intel.jndn.management.types.RibEntry;
+import com.intel.jndn.management.types.Route;
+
+import net.named_data.nfd.utils.Nfdc;
+import net.named_data.nfd.utils.NfdcAsyncTask;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RouteListActivity extends Activity
+{
+  @Override
+  public void
+  onCreate(Bundle savedInstanceState)
+  {
+    super.onCreate(savedInstanceState);
+
+    FragmentManager fm = getFragmentManager();
+    FragmentTransaction ft = fm.beginTransaction();
+    if (fm.findFragmentById(android.R.id.content) == null) {
+      ft.add(android.R.id.content, m_routeListFragment);
+    }
+    else {
+      ft.replace(android.R.id.content, m_routeListFragment);
+    }
+    ft.commit();
+  }
+
+  public static class RouteListFragment extends ListFragment {
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState)
+    {
+      super.onActivityCreated(savedInstanceState);
+
+      RouteListAdapter adapter = new RouteListAdapter(getActivity());
+      setListAdapter(adapter);
+
+      adapter.updateFaceList();
+    }
+
+    private class RouteListAdapter extends BaseAdapter
+    {
+      public RouteListAdapter(Context context)
+      {
+        this.m_inflater = LayoutInflater.from(context);
+        this.m_context = context;
+      }
+
+      public void
+      updateFaceList()
+      {
+        new NfdcAsyncTask(m_context,
+                          new NfdcAsyncTask.Task() {
+                            public String
+                            runTask() throws Exception
+                            {
+                              synchronized (m_routesLock) {
+                                Nfdc nfdc = new Nfdc();
+                                m_routes = nfdc.ribList();
+                                nfdc.shutdown();
+                              }
+
+                              getActivity().runOnUiThread(new Runnable() {
+                                @Override
+                                public void run()
+                                {
+                                  notifyDataSetChanged();
+                                }
+                              });
+                              return null;
+                            }
+                          }).execute();
+      }
+
+      @Override
+      public int
+      getCount()
+      {
+        synchronized (m_routesLock) {
+          if (m_routes == null)
+            return 0;
+          else
+            return m_routes.size();
+        }
+      }
+
+      @Override
+      public Object
+      getItem(int position)
+      {
+        synchronized (m_routesLock) {
+          assert m_routes != null && position < m_routes.size();
+          return m_routes.get(position);
+        }
+      }
+
+      @Override
+      public long
+      getItemId(int position)
+      {
+        return position;
+      }
+
+      @Override
+      public View
+      getView(int position, View convertView, ViewGroup parent)
+      {
+        RouteListItemViewHolder holder;
+        if (convertView == null) {
+          convertView = this.m_inflater.inflate(android.R.layout.simple_list_item_2, parent, false);
+          holder = new RouteListItemViewHolder(convertView);
+          convertView.setTag(holder);
+        } else {
+          holder = (RouteListItemViewHolder)convertView.getTag();
+        }
+
+        RibEntry e;
+        synchronized (m_routesLock) {
+          e = m_routes.get(position);
+        }
+        holder.text1.setText(e.getName().toUri());
+
+        List<String> faceList = new ArrayList<>();
+        for (Route r : e.getRoutes()) {
+          faceList.add(String.valueOf(r.getFaceId()));
+        }
+        holder.text2.setText(StringUtils.join(faceList, ", "));
+
+        return convertView;
+      }
+
+      /////////////////////////////////////////////////////////////////////////
+      private LayoutInflater m_inflater;
+      private Context m_context;
+      private List<RibEntry> m_routes;
+      private final Object m_routesLock = new Object();
+
+    }
+
+    private static class RouteListItemViewHolder
+    {
+      RouteListItemViewHolder(View v)
+      {
+        text1 = (TextView)v.findViewById(android.R.id.text1);
+        text2 = (TextView)v.findViewById(android.R.id.text2);
+      }
+
+      /////////////////////////////////////////////////////////////////////////
+      public TextView text1;
+      public TextView text2;
+    }
+  }
+
+  /////////////////////////////////////////////////////////////////////////////
+  private RouteListFragment m_routeListFragment = new RouteListFragment();
+}
diff --git a/app/src/main/java/net/named_data/nfd/utils/Nfdc.java b/app/src/main/java/net/named_data/nfd/utils/Nfdc.java
index a266ef2..5517449 100644
--- a/app/src/main/java/net/named_data/nfd/utils/Nfdc.java
+++ b/app/src/main/java/net/named_data/nfd/utils/Nfdc.java
@@ -21,8 +21,11 @@
 
 import com.intel.jndn.management.NFD;
 import com.intel.jndn.management.types.FaceStatus;
+import com.intel.jndn.management.types.RibEntry;
 
+import net.named_data.jndn.ControlParameters;
 import net.named_data.jndn.Face;
+import net.named_data.jndn.ForwardingFlags;
 import net.named_data.jndn.Name;
 import net.named_data.jndn.security.*;
 import net.named_data.jndn.security.identity.IdentityManager;
@@ -69,8 +72,8 @@
   }
 
   /**
-   * @brief Adds a nexthop to a FIB entry
-   *
+   * Adds a nexthop to a FIB entry
+   * <p>
    * If the FIB entry does not exist, it is inserted automatically
    */
   public void
@@ -79,36 +82,56 @@
   }
 
   /**
-   * @brief Removes a nexthop from an existing FIB entry
-   *
+   * Removes a nexthop from an existing FIB entry
+   * <p>
    * If the last nexthop record in a FIB entry is removed, the FIB entry is also deleted
    */
   public void
   fibRemoveNextHop(Name prefix, int faceId)
   {
-
   }
 
   /**
-   * @brief Registers name to the given faceId or faceUri
+   * Registers name to the given faceId or faceUri
+   */
+  public boolean
+  ribRegisterPrefix(Name prefix, int faceId, int cost, boolean isChildInherit, boolean isCapture) throws Exception
+  {
+    ForwardingFlags flags = new ForwardingFlags();
+    flags.setChildInherit(isChildInherit);
+    flags.setCapture(isCapture);
+    return NFD.register(m_face,
+                        new ControlParameters()
+                          .setName(prefix)
+                          .setFaceId(faceId)
+                          .setCost(cost)
+                          .setForwardingFlags(flags));
+  }
+
+  /**
+   * Unregisters name from the given faceId/faceUri
    */
   public void
-  ribRegisterPrefix(Name prefix, int faceId, int cost, boolean isChildInherit, boolean isCapture)
+  ribUnregisterPrefix(Name prefix, int faceId) throws Exception
   {
+    NFD.unregister(m_face,
+                   new ControlParameters()
+                     .setName(prefix)
+                     .setFaceId(faceId));
   }
 
   /**
-   * @brief Unregisters name from the given faceId/faceUri
+   * List all of routes (RIB entries)
    */
-  public void
-  ribUnregisterPrefix(Name prefix, int faceIdName)
+  public List<RibEntry>
+  ribList() throws Exception
   {
-
+    return NFD.getRouteList(m_face);
   }
 
   /**
-   * @brief Creates new face
-   *
+   * Creates new face
+   * <p>
    * This command allows creation of UDP unicast and TCP faces only
    */
   public int
@@ -118,7 +141,7 @@
   }
 
   /**
-   * @brief Destroys face
+   * Destroys face
    */
   public void
   faceDestroy(int faceId) throws Exception
@@ -127,7 +150,7 @@
   }
 
   /**
-   * @brief List all faces
+   * List all faces
    */
   public List<FaceStatus>
   faceList() throws Exception
@@ -136,16 +159,15 @@
   }
 
   /**
-   * @brief Sets the strategy for a namespace
+   * Sets the strategy for a namespace
    */
   public void
   strategyChoiceSet(Name namespace, Name strategy)
   {
-
   }
 
   /**
-   * @brief Unset the strategy for a namespace
+   * Unset the strategy for a namespace
    */
   public void
   strategyChoiceUnset(Name namespace)
diff --git a/app/src/main/res/layout/create_route.xml b/app/src/main/res/layout/create_route.xml
new file mode 100644
index 0000000..ddf7fb2
--- /dev/null
+++ b/app/src/main/res/layout/create_route.xml
@@ -0,0 +1,40 @@
+<?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="wrap_content">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@style/dialog_margin"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="Enter prefix and FaceUri to register with NDN daemon"
+        android:layout_gravity="center_horizontal"/>
+
+    <EditText
+        android:id="@+id/prefix"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/dialog_margin"
+        android:hint="Prefix"
+        android:inputType="text"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:selectAllOnFocus="true">
+        <requestFocus />
+    </EditText>
+
+    <EditText
+        android:id="@+id/faceUri"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/dialog_margin"
+        android:hint="Face URI"
+        android:inputType="text"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:selectAllOnFocus="true">
+     </EditText>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/xml/pref_routes.xml b/app/src/main/res/xml/pref_routes.xml
new file mode 100644
index 0000000..322f584
--- /dev/null
+++ b/app/src/main/res/xml/pref_routes.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+  <PreferenceCategory
+      android:key="routes"
+      android:title="Routes">
+
+    <Preference android:title="Create route" android:key="create_route" />
+
+    <PreferenceScreen android:title="List routes" android:key="list_routes">
+        <intent android:action="android.intent.action.VIEW"
+                android:targetPackage="net.named_data.nfd"
+                android:targetClass="net.named_data.nfd.RouteListActivity" />
+    </PreferenceScreen>
+
+  </PreferenceCategory>
+</PreferenceScreen>