service: NFD Android service starts and stops NFD

Change-Id: I9351c669282c3c02fd533237489beeb10fe7d32f
Refs: #2431
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85ba551..9fad6dd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,11 +3,7 @@
     package="net.named_data.nfd" >
 
     <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.READ_LOGS" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 
     <application
         android:allowBackup="true"
@@ -30,13 +26,13 @@
         </activity>
 
         <service
-            android:name=".NfdService"
-            android:process="net.named_data.nfd.NfdService"
+            android:name=".service.NfdService"
+            android:process="net.named_data.nfd.service.NfdService"
             android:icon="@drawable/ic_launcher"
             android:label="@string/service_name"
             android:exported="true" >
             <intent-filter>
-                <action android:name="net.named_data.nfd.NfdService" />
+                <action android:name="net.named_data.nfd.START_NFD_SERVICE" />
             </intent-filter>
         </service>
 
diff --git a/app/src/main/java/net/named_data/nfd/NfdMainActivity.java b/app/src/main/java/net/named_data/nfd/NfdMainActivity.java
index 76711b4..e9a5165 100644
--- a/app/src/main/java/net/named_data/nfd/NfdMainActivity.java
+++ b/app/src/main/java/net/named_data/nfd/NfdMainActivity.java
@@ -18,15 +18,23 @@
 
 package net.named_data.nfd;
 
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
+import android.content.ServiceConnection;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
 import android.support.v7.app.ActionBarActivity;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.Button;
 
 import net.named_data.nfd.service.NfdService;
-import net.named_data.nfd.wrappers.NfdWrapper;
 
 public class NfdMainActivity extends ActionBarActivity {
 
@@ -34,8 +42,32 @@
   protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
+
+    // Get UI Elements
+    m_nfdButton = (Button) findViewById(R.id.nfd_button);
   }
 
+  @Override
+  protected void onResume() {
+    super.onResume();
+
+    // Bind to NfdService
+    bindNfdService();
+  }
+
+  @Override
+  protected void onPause() {
+    super.onPause();
+
+    // Unbind from NfdService
+    unbindNfdService();
+  }
+
+  @Override
+  protected void onDestroy() {
+    G.Log("MainActivity::onDestroy()");
+    super.onDestroy();
+  }
 
   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
@@ -59,36 +91,228 @@
     return super.onOptionsItemSelected(item);
   }
 
-  public void startNFD(View view) {
-    G.Log("Starting NFD ...");
-
-    Intent intent = new Intent(this, NfdService.class);
-    startService(intent);
+  public void toggleNfdState(View view) {
+    toggleNfdState();
   }
 
-  public void stopNFD(View view) {
-    G.Log("Stopping NFD ...");
+  /**
+   * Thread safe way to start and stop the NFD through
+   * the UI Button.
+   */
+  private synchronized void toggleNfdState() {
+    disableNfdButton();
 
-    Intent intent = new Intent(this, NfdService.class);
-    stopService(intent);
+    if (m_isNfdRunning) {
+      m_nfdButton.setText(R.string.stopping_nfd);
+      sendNfdServiceMessage(NfdService.MESSAGE_STOP_NFD_SERVICE);
+    } else {
+      m_nfdButton.setText(R.string.starting_nfd);
+      sendNfdServiceMessage(NfdService.MESSAGE_START_NFD_SERVICE);
+    }
   }
 
-  public void startNFDexplicit(View view) {
-    G.Log("Starting NFD explicitly ...");
-
-    Intent intent = new Intent("net.named_data.nfd.NfdService");
-    startService(intent);
+  /**
+   * Convenience method to send a message to the NfdService
+   * through a Messenger.
+   *
+   * @param message Message from a set of predefined NfdService messages.
+   */
+  private synchronized void sendNfdServiceMessage(int message) {
+    try {
+      Message msg = Message.obtain(null, message);
+      msg.replyTo = m_clientMessenger;
+      m_nfdServiceMessenger.send(msg);
+    } catch (RemoteException e) {
+      // If Service crashes, nothing to do here
+      G.Log("Service Disconnected: " + e);
+    }
   }
 
-  public void startNfd(View view) {
-    G.Log("Starting NFD through JNI ...");
-
-    NfdWrapper.startNfd(getFilesDir().getAbsolutePath());
+  /**
+   * @brief Enable UI Button once critical operations are completed.
+   */
+  private void enableNfdButton() {
+    m_nfdButton.setEnabled(true);
   }
 
-  public void stopNfd(View view) {
-    G.Log("Stopping NFD through JNI ...");
-
-    NfdWrapper.stopNfd();
+  /**
+   * @brief Disable UI Button to ensure user is unable to hit the button mutiple times.
+   */
+  private void disableNfdButton() {
+    m_nfdButton.setEnabled(false);
   }
+
+  /**
+   * @brief Thread safe way of flagging that the NFD is running.
+   *
+   * @param isNfdRunning true if NFD is running; false otherwise
+   */
+  private synchronized void setNfdRunningState(boolean isNfdRunning) {
+    m_isNfdRunning = isNfdRunning;
+  }
+
+  /**
+   * @brief Toggle UI Button text to inform user of the next possible action.
+   *
+   * @param isNfdRunning true if NFD is currently running; false otherwise
+   */
+  private void setNfdButtonText(boolean isNfdRunning) {
+    m_nfdButton.setText(isNfdRunning ?
+      R.string.stop_nfd :
+      R.string.start_nfd);
+  }
+
+  /**
+   * @brief Thread safe way of flagging that application is successfully connected
+   * to the NfdService.
+   *
+   * @param isNfdServiceConnected true if successfully connected to the NfdService;
+   *                              false otherwise
+   */
+  private synchronized void setNfdServiceConnected(boolean isNfdServiceConnected) {
+    m_isNfdServiceConnected = isNfdServiceConnected;
+  }
+
+  /**
+   * @brief Method that binds the current activity to the NfdService.
+   */
+  private synchronized void bindNfdService() {
+    if (m_isNfdServiceBound == false) {
+      // Bind to Service
+      m_isNfdServiceBound = this.bindService(
+        new Intent(this, NfdService.class),
+        m_ServiceConnection, Context.BIND_AUTO_CREATE);
+
+      G.Log("MainActivity::bindNfdService()");
+    }
+  }
+
+  /**
+   * @brief Method that unbinds the current activity from the NfdService.
+   */
+  private synchronized void unbindNfdService() {
+    if (m_isNfdServiceBound == true) {
+      // Unbind from Service
+      this.unbindService(m_ServiceConnection);
+      m_isNfdServiceBound = false;
+
+      G.Log("MainActivity::unbindNfdService()");
+    }
+  }
+
+  /**
+   * @brief Client Message Handler.
+   *
+   * This handler is used to handle messages that are being sent back
+   * from the NfdService to the current application.
+   */
+  class ClientHandler extends Handler {
+    @Override
+    public void handleMessage(Message msg) {
+      switch (msg.what) {
+      case NfdService.MESSAGE_NFD_RUNNING:
+        setNfdRunningState(true);
+        setNfdButtonText(true);
+        G.Log("ClientHandler: NFD is Running.");
+        break;
+
+      case NfdService.MESSAGE_NFD_STOPPED:
+        setNfdRunningState(false);
+        setNfdButtonText(false);
+        G.Log("ClientHandler: NFD is Stopped.");
+        break;
+
+      default:
+        super.handleMessage(msg);
+        break;
+      }
+
+      enableNfdButton();
+    }
+  }
+
+  /**
+   * @brief Client ServiceConnection to NfdService.
+   */
+  private ServiceConnection m_ServiceConnection = new ServiceConnection() {
+    @Override
+    public void onServiceConnected(ComponentName className, IBinder service) {
+      // Establish Messenger to the Service
+      m_nfdServiceMessenger = new Messenger(service);
+
+      // Set service connected flag
+      setNfdServiceConnected(true);
+
+      // Check if NFD Service is running
+      try {
+        Message msg = Message.obtain(null,
+          NfdService.MESSAGE_IS_NFD_RUNNING);
+        msg.replyTo = m_clientMessenger;
+        m_nfdServiceMessenger.send(msg);
+      } catch (RemoteException e) {
+        // If Service crashes, nothing to do here
+        G.Log("onServiceConnected(): " + e);
+      }
+
+      G.Log("m_ServiceConnection::onServiceConnected()");
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName componentName) {
+      // In event of unexpected disconnection with the Service; Not expecting to get here.
+      G.Log("m_ServiceConnection::onServiceDisconnected()");
+
+      // Update UI
+      disableNfdButton();
+      m_nfdButton.setText(R.string.reconnect_to_nfd);
+
+      // Reconnect to NfdService
+      setNfdServiceConnected(false);
+      retryConnectionToNfdService();
+    }
+  };
+
+  /**
+   * @brief Attempt to reconnect to the NfdService.
+   *
+   * This method attempts to reconnect the application to the NfdService
+   * when the NfdService has been killed (either by the user or by the OS).
+   */
+  private void retryConnectionToNfdService() {
+    new Thread(){
+      @Override
+      public void run() {
+        while (m_isNfdServiceConnected == false) {
+          G.Log("Retrying connection to NFD Service ...");
+          bindNfdService();
+
+          try {
+            Thread.sleep(1000);
+          } catch (InterruptedException e) {
+            // Nothing to do here; Keep going.
+          }
+        }
+
+        G.Log("Reconnection to NFD Service is successful.");
+      }
+    }.start();
+  }
+
+  /** Flag that marks that application is bound to the NfdService */
+  private boolean m_isNfdServiceBound = false;
+
+  /** Flag that marks that application is connected to the NfdService */
+  private boolean m_isNfdServiceConnected = false;
+
+  /** Client Message Handler */
+  private final Messenger m_clientMessenger = new Messenger(new ClientHandler());
+
+  /** Messenger connection to NfdService */
+  private Messenger m_nfdServiceMessenger = null;
+
+  /** Flag that makrs if the NFD is running */
+  private boolean m_isNfdRunning = false;
+
+  /** Button that starts and stops the NFD */
+  private Button m_nfdButton;
 }
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 1e2f7c6..d7f6ad9 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,32 +20,78 @@
 
 import android.app.Service;
 import android.content.Intent;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.Messenger;
+import android.os.RemoteException;
 
 import net.named_data.nfd.G;
 
+/**
+ * @brief NfdService that runs the native NFD.
+ *
+ * NfdSevice 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 {
 
-  private final Messenger mNfdServiceMessenger
-      = new Messenger(new NfdServiceMessageHandler(this));
+  /**
+   * @brief Loading of NFD Native libraries.
+   */
+  static {
+    System.loadLibrary("nfd-wrapper");
+  }
+
+  /**
+   * @brief Native API for starting the NFD.
+   *
+   * @param homePath Absolute path of the home directory for the service;
+   *                 Usually achieved by calling ContextWrapper.getFilesDir().getAbsolutePath()
+   */
+  public native static void
+  startNfd(String homePath);
+
+  /**
+   * @brief Native API for stopping the NFD.
+   */
+  public native static void
+  stopNfd();
+
+  /** Message to start NFD Service */
+  public static final int MESSAGE_START_NFD_SERVICE = 1;
+
+  /** Message to stop NFD Service */
+  public static final int MESSAGE_STOP_NFD_SERVICE = 2;
+
+  /** Message to query if NFD is running */
+  public static final int MESSAGE_IS_NFD_RUNNING = 3;
+
+  /** Message to indicate NFD is running */
+  public static final int MESSAGE_NFD_RUNNING = 4;
+
+  /** Message to indicate NFD is stopped */
+  public static final int MESSAGE_NFD_STOPPED = 5;
 
   @Override
   public void onCreate() {
-    G.Log("onCreate()");
-    // TODO: Reload NFD and NRD in memory structures (if any)
+    G.Log("NFDService::onCreate()");
   }
 
   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
-    G.Log("onStartCommand()");
+    G.Log("NFDService::onStartCommand()");
+
     // If we need to handle per-client start invocations, they are to be
     // handled here.
 
-    // Nothing else to do here for now.
+    // Start NFD
+    serviceStartNfd();
 
     // Service is restarted when killed.
-    // Pending intents delivered; null intent redelivered otherwise
+    // Pending intents delivered; null intent redelivered otherwise.
     return START_STICKY;
   }
 
@@ -59,12 +105,111 @@
    */
   @Override
   public IBinder onBind(Intent intent) {
-    return mNfdServiceMessenger.getBinder();
+    return m_nfdServiceMessenger.getBinder();
   }
 
   @Override
   public void onDestroy() {
-    G.Log("onDestroy()");
-    // TODO: Save NFD and NRD in memory data structures.
+    G.Log("NFDService::onDestroy()");
+
+    if (m_isNfdStarted) {
+      G.Log("Stopping NFD ...");
+      serviceStopNfd();
+    }
   }
+
+  /**
+   * @brief Thread safe way of starting the NFD and updating the
+   * started flag.
+   */
+  private synchronized void serviceStartNfd() {
+    if (m_isNfdStarted == false) {
+      m_isNfdStarted = true;
+      startNfd(getFilesDir().getAbsolutePath());
+
+      // TODO: Reload NFD and NRD in memory structures (if any)
+
+      // Keep Service alive; In event when service is started
+      // from a Handler's message through binding with the service.
+      startService(new Intent(this, NfdService.class));
+
+      G.Log("serviceStartNfd()");
+    } else {
+      G.Log("serviceStartNfd(): NFD Service already running!");
+    }
+  }
+
+  /**
+   * @brief Thread safe way of stopping the NFD and updating the
+   * started flag.
+   */
+  private synchronized void serviceStopNfd() {
+    if (m_isNfdStarted == true) {
+      m_isNfdStarted = false;
+
+      // TODO: Save NFD and NRD in memory data structures.
+      stopNfd();
+
+      stopSelf();
+      G.Log("serviceStopNfd()");
+    }
+  }
+
+  /**
+   * @brief Thread safe way of checking if the the NFD is running.
+   *
+   * @return true if NFD is running; false otherwise.
+   */
+  private synchronized boolean isNfdRunning() {
+    return m_isNfdStarted;
+  }
+
+  /**
+   * @brief Message handler for the the NFD Service.
+   */
+  class NfdServiceMessageHandler extends Handler {
+
+    @Override
+    public void handleMessage(Message message) {
+      switch (message.what) {
+      case NfdService.MESSAGE_START_NFD_SERVICE:
+        serviceStartNfd();
+        replyToClient(message, NfdService.MESSAGE_NFD_RUNNING);
+        break;
+
+      case NfdService.MESSAGE_STOP_NFD_SERVICE:
+        serviceStopNfd();
+        replyToClient(message, NfdService.MESSAGE_NFD_STOPPED);
+        break;
+
+      case NfdService.MESSAGE_IS_NFD_RUNNING:
+        int replyMessage = isNfdRunning() ?
+          NfdService.MESSAGE_NFD_RUNNING :
+          NfdService.MESSAGE_NFD_STOPPED;
+
+        replyToClient(message, replyMessage);
+        break;
+
+      default:
+        super.handleMessage(message);
+        break;
+      }
+    }
+
+    private void replyToClient(Message message, int replyMessage) {
+      try {
+        message.replyTo.send(Message.obtain(null, replyMessage));
+      } catch (RemoteException e) {
+        // Nothing to do here; It means that client end has been terminated.
+      }
+    }
+  }
+
+  /** Messenger to handle messages that are passed to the NfdService */
+  private final Messenger m_nfdServiceMessenger
+    = new Messenger(new NfdServiceMessageHandler());
+
+  /** Flag that denotes if the NFD has been started */
+  private boolean m_isNfdStarted = false;
+
 }
diff --git a/app/src/main/java/net/named_data/nfd/service/NfdServiceMessageConstants.java b/app/src/main/java/net/named_data/nfd/service/NfdServiceMessageConstants.java
deleted file mode 100644
index 04239a5..0000000
--- a/app/src/main/java/net/named_data/nfd/service/NfdServiceMessageConstants.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd.service;
-
-/**
- * Message constants that can be sent to the NFD Service for processing.
- */
-public class NfdServiceMessageConstants {
-
-  /** Message to start NFD Service. */
-  public static final int MESSAGE_START_NFD_SERVICE = 1;
-
-}
diff --git a/app/src/main/java/net/named_data/nfd/service/NfdServiceMessageHandler.java b/app/src/main/java/net/named_data/nfd/service/NfdServiceMessageHandler.java
deleted file mode 100644
index 7efd6f9..0000000
--- a/app/src/main/java/net/named_data/nfd/service/NfdServiceMessageHandler.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd.service;
-
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-
-/**
- * Message handler for the the NFD Service.
- */
-class NfdServiceMessageHandler extends Handler {
-
-  private NfdService mNfdService;
-
-  NfdServiceMessageHandler(NfdService nfdService) {
-    mNfdService = nfdService;
-  }
-
-  @Override
-  public void handleMessage(Message message) {
-    switch (message.what) {
-      case NfdServiceMessageConstants.MESSAGE_START_NFD_SERVICE:
-        break;
-      default:
-        super.handleMessage(message);
-        break;
-    }
-  }
-}
diff --git a/app/src/main/java/net/named_data/nfd/wrappers/NfdWrapper.java b/app/src/main/java/net/named_data/nfd/wrappers/NfdWrapper.java
deleted file mode 100644
index 48172da..0000000
--- a/app/src/main/java/net/named_data/nfd/wrappers/NfdWrapper.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (c) 2015 Regents of the University of California
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon) Android.
- * See AUTHORS.md for complete list of NFD Android authors and contributors.
- *
- * NFD Android is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD Android, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.named_data.nfd.wrappers;
-
-public class NfdWrapper {
-    static {
-       System.loadLibrary("nfd-wrapper");
-    }
-
-    public native static void
-    startNfd(String homePath);
-
-    public native static void
-    stopNfd();
-}
diff --git a/app/src/main/jni/nfd-wrapper.cpp b/app/src/main/jni/nfd-wrapper.cpp
index 5142965..c0508ba 100644
--- a/app/src/main/jni/nfd-wrapper.cpp
+++ b/app/src/main/jni/nfd-wrapper.cpp
@@ -189,7 +189,7 @@
 } // namespace nfd
 
 JNIEXPORT void JNICALL
-Java_net_named_1data_nfd_wrappers_NfdWrapper_startNfd(JNIEnv* env, jclass, jstring homePathJ)
+Java_net_named_1data_nfd_service_NfdService_startNfd(JNIEnv* env, jclass, jstring homePathJ)
 {
   if (nfd::g_runner.get() == nullptr) {
     // set/update HOME environment variable
@@ -223,7 +223,7 @@
 }
 
 JNIEXPORT void JNICALL
-Java_net_named_1data_nfd_wrappers_NfdWrapper_stopNfd(JNIEnv*, jclass)
+Java_net_named_1data_nfd_service_NfdService_stopNfd(JNIEnv*, jclass)
 {
   if (nfd::g_runner.get() != nullptr) {
     NFD_LOG_INFO("Stopping NFD...");
diff --git a/app/src/main/jni/nfd-wrapper.hpp b/app/src/main/jni/nfd-wrapper.hpp
index 2b9523e..97e3cc1 100644
--- a/app/src/main/jni/nfd-wrapper.hpp
+++ b/app/src/main/jni/nfd-wrapper.hpp
@@ -19,28 +19,28 @@
 
 /* DO NOT EDIT THIS FILE - it is machine generated */
 #include <jni.h>
-/* Header for class net_named_data_nfd_wrappers_NfdWrapper */
+/* Header for class net_named_data_nfd_service_NfdService */
 
-#ifndef _Included_net_named_data_nfd_wrappers_NfdWrapper
-#define _Included_net_named_data_nfd_wrappers_NfdWrapper
+#ifndef _Included_net_named_data_nfd_service_NfdService
+#define _Included_net_named_data_nfd_service_NfdService
 #ifdef __cplusplus
 extern "C" {
 #endif
 /*
- * Class:     net_named_data_nfd_wrappers_NfdWrapper
+ * Class:     net_named_data_nfd_service_NfdService
  * Method:    startNfd
- * Signature: ()V
+ * Signature: (Ljava/lang/String;)V
  */
 JNIEXPORT void JNICALL
-Java_net_named_1data_nfd_wrappers_NfdWrapper_startNfd(JNIEnv*, jclass, jstring);
+Java_net_named_1data_nfd_service_NfdService_startNfd(JNIEnv*, jclass, jstring);
 
 /*
- * Class:     net_named_data_nfd_wrappers_NfdWrapper
+ * Class:     net_named_data_nfd_service_NfdService
  * Method:    stopNfd
  * Signature: ()V
  */
 JNIEXPORT void JNICALL
-Java_net_named_1data_nfd_wrappers_NfdWrapper_stopNfd(JNIEnv*, jclass);
+Java_net_named_1data_nfd_service_NfdService_stopNfd(JNIEnv*, jclass);
 
 #ifdef __cplusplus
 }
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 289db7d..cb17103 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -7,41 +7,11 @@
     android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
 
     <Button
-        android:id="@+id/start_nfd_button"
+        android:id="@+id/nfd_button"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:onClick="startNFD"
-        android:text="Start NFD" />
+        android:onClick="toggleNfdState"
+        android:enabled="false"
+        android:text="Checking on NFD Service ..." />
 
-    <Button
-        android:id="@+id/stop_nfd_button"
-        android:layout_below="@id/start_nfd_button"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:onClick="stopNFD"
-        android:text="Stop NFD" />
-
-    <Button
-        android:id="@+id/start_nfd_explicit_button"
-        android:layout_below="@id/stop_nfd_button"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:onClick="startNFDexplicit"
-        android:text="Start NFD explicit!" />
-
-    <Button
-        android:id="@+id/start_nfd_jni"
-        android:layout_below="@id/start_nfd_explicit_button"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:onClick="startNfd"
-        android:text="Start NFD through JNI" />
-
-    <Button
-        android:id="@+id/stop_nfd_jni"
-        android:layout_below="@id/start_nfd_jni"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:onClick="stopNfd"
-        android:text="Stop NFD through JNI" />
 </RelativeLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e39585d..601b73f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,4 +2,10 @@
     <string name="app_name">NFD</string>
     <string name="service_name">NFD Service</string>
     <string name="action_settings">Settings</string>
+    <string name="stop_nfd">Stop NFD</string>
+    <string name="start_nfd">Start NFD</string>
+    <string name="reconnecting_to_nfd">Please wait while we connect to the NFD Service.</string>
+    <string name="stopping_nfd">Stopping NFD ...</string>
+    <string name="starting_nfd">Starting NFD ...</string>
+    <string name="reconnect_to_nfd">Reconnecting to NFD Service</string>
 </resources>