Add ad hoc WiFi communication function
Change-Id: I6ec986708bfa2dcdf1cc2ed121f249fabd99ac86
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index d2b0a45..6e87640 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,6 +1,15 @@
Release Notes
=============
+## Version 0.2.4
+
+- Introduce function to quickly enable local NDN communication using Ad Hoc WiFi
+
+- Fix a small bug in `ndn` command-line tool
+
+- The bundled NDN stack binaries moved to `NDN.app/Contents/Helpers` folder to
+ respect macOS code signing conventions.
+
## Version 0.2.3
- Correct names of tray menu items
diff --git a/make-osx-bundle.py b/make-osx-bundle.py
index dfbbbe4..db61242 100755
--- a/make-osx-bundle.py
+++ b/make-osx-bundle.py
@@ -370,6 +370,7 @@
parser.add_option('-r', '--release', dest='release', help='Build a release. This determines the version number of the release.')
parser.add_option('-s', '--snapshot', dest='snapshot', help='Build a snapshot release. This determines the \'snapshot version\'.')
parser.add_option('-g', '--git', dest='git', help='Build a snapshot release. Use the git revision number as the \'snapshot version\'.', action='store_true', default=False)
+ parser.add_option('--no-dmg', dest='no_dmg', action='store_true', default=False, help='''Disable creation of DMG''')
parser.add_option('--codesign', dest='codesign', help='Identity to use for code signing. (If not set, no code signing will occur)')
options, args = parser.parse_args()
@@ -405,17 +406,18 @@
codesign(a.bundle)
print ''
- # Create diskimage
- title = "NDN-%s" % ver
- fn = "build/%s.dmg" % title
- d = DiskImage(fn, title)
- d.symlink('/Applications', '/Applications')
- d.copy('build/NDN.app', '/NDN.app')
- d.create()
+ if not options.no_dmg:
+ # Create diskimage
+ title = "NDN-%s" % ver
+ fn = "build/%s.dmg" % title
+ d = DiskImage(fn, title)
+ d.symlink('/Applications', '/Applications')
+ d.copy('build/NDN.app', '/NDN.app')
+ d.create()
- if options.codesign:
- print ' * Signing .dmg with identity `%s\'' % options.codesign
- codesign(fn)
- print ''
+ if options.codesign:
+ print ' * Signing .dmg with identity `%s\'' % options.codesign
+ codesign(fn)
+ print ''
Popen('tail -n +3 RELEASE_NOTES.md | pandoc -f markdown -t html > build/release-notes-%s.html' % ver, shell=True).wait()
diff --git a/ndn-control-center.xml b/ndn-control-center.xml
index 2436d76..ca26bf1 100644
--- a/ndn-control-center.xml
+++ b/ndn-control-center.xml
@@ -34,5 +34,11 @@
<ns0:minimumSystemVersion>10.12.0</ns0:minimumSystemVersion>
<enclosure length="27659751" type="application/octet-stream" url="https://named-data.net/binaries/NDN-Control-Center/NDN-0.2.3.dmg" ns0:dsaSignature="MC4CFQCLben2hhIZr01C9qo84yBs9fQaWwIVAJ0Amf0kXPDHqxD/Ce5AC1awzpgZ" ns0:shortVersionString="0.2.3" ns0:version="0.2.3" />
<ns0:releaseNotesLink>https://named-data.net/binaries/NDN-Control-Center/release-notes-0.2.3.html</ns0:releaseNotesLink></item>
+<item>
+<title>Version 0.2.4 (ndn-cxx version 0.5.1-34-g1709aa7, NFD version 0.5.1-38-gd396b61, ndn-tools version 0.4)</title>
+<pubDate>Sat, 18 Mar 2017 19:05:28 -0700</pubDate>
+<ns0:minimumSystemVersion>10.12.0</ns0:minimumSystemVersion>
+<enclosure length="27751651" type="application/octet-stream" url="https://named-data.net/binaries/NDN-Control-Center/NDN-0.2.4.dmg" ns0:dsaSignature="MC0CFFu7Gz4ue3ESbVfA3/5ak1BsDsevAhUAlJO7SdLhWK8ZtW2SvoA8yomDwIg=" ns0:shortVersionString="0.2.4" ns0:version="0.2.4" />
+<ns0:releaseNotesLink>https://named-data.net/binaries/NDN-Control-Center/release-notes-0.2.4.html</ns0:releaseNotesLink></item>
</channel>
-</rss>
\ No newline at end of file
+</rss>
diff --git a/res/icon-connected-adhoc-black.png b/res/icon-connected-adhoc-black.png
new file mode 100644
index 0000000..3251fd9
--- /dev/null
+++ b/res/icon-connected-adhoc-black.png
Binary files differ
diff --git a/src/main.cpp b/src/main.cpp
index 1dcee21..b3144c7 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -40,7 +40,6 @@
public:
Ncc()
: m_isActive(true)
- , m_localhopFibEntry(Name("/localhop/nfd"))
, m_face(nullptr, m_keyChain)
, m_controller(m_face, m_keyChain)
, m_scheduler(m_face.getIoService())
@@ -103,12 +102,17 @@
onFibStatusRetrieved(const std::vector<nfd::FibEntry>& status)
{
bool isConnectedToHub = false;
+ bool isConnectedToAdhoc = false;
for (auto const& fibEntry : status) {
if (fibEntry.getPrefix() == m_localhopFibEntry) {
isConnectedToHub = true;
}
+ else if (fibEntry.getPrefix() == m_adhocFibEntry) {
+ isConnectedToAdhoc = true;
+ }
}
emit m_tray.connectivityUpdate(isConnectedToHub);
+ emit m_tray.adhocUpdate(isConnectedToAdhoc);
}
void
@@ -141,7 +145,8 @@
private:
volatile bool m_isActive;
boost::thread m_nfdThread;
- const Name m_localhopFibEntry;
+ const Name m_localhopFibEntry = "/localhop/nfd";
+ const Name m_adhocFibEntry = "/adhoc";
KeyChain m_keyChain;
Face m_face;
diff --git a/src/osx-adhoc.hpp b/src/osx-adhoc.hpp
new file mode 100644
index 0000000..10019dd
--- /dev/null
+++ b/src/osx-adhoc.hpp
@@ -0,0 +1,70 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017, Regents of the University of California,
+ *
+ * This file is part of NFD Control Center. See AUTHORS.md for complete list of NFD
+ * authors and contributors.
+ *
+ * NFD Control Center 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 Control Center 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
+ * Control Center, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NCC_OSX_ADHOC_HPP
+#define NCC_OSX_ADHOC_HPP
+
+#include "config.hpp"
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/mgmt/nfd/controller.hpp>
+#include <ndn-cxx/mgmt/nfd/face-monitor.hpp>
+
+#ifdef OSX_BUILD
+#define ADHOC_SUPPORTED 1
+#endif // OSX_BUILD
+
+#ifdef ADHOC_SUPPORTED
+
+namespace ndn {
+namespace ncc {
+
+class Adhoc
+{
+public:
+ Adhoc(Face& face, KeyChain& keychain);
+
+ ~Adhoc();
+
+ bool
+ createAdhoc();
+
+ void
+ onNotification(const ndn::nfd::FaceEventNotification& notification);
+
+ void
+ destroyAdhoc();
+
+private:
+ void
+ unregisterRoutes(uint16_t origin);
+
+private:
+ Face& m_face;
+ KeyChain& m_keyChain;
+ nfd::Controller* m_controller;
+ nfd::FaceMonitor* m_faceMonitor;
+};
+
+} // namespace ncc
+} // namespace ndn
+
+#endif // ADHOC_SUPPORTED
+
+#endif // NCC_OSX_ADHOC_HPP
diff --git a/src/osx-adhoc.mm b/src/osx-adhoc.mm
new file mode 100644
index 0000000..fcaf146
--- /dev/null
+++ b/src/osx-adhoc.mm
@@ -0,0 +1,142 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017, Regents of the University of California,
+ *
+ * This file is part of NFD Control Center. See AUTHORS.md for complete list of NFD
+ * authors and contributors.
+ *
+ * NFD Control Center 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 Control Center 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
+ * Control Center, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "osx-adhoc.hpp"
+
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
+
+#ifdef OSX_BUILD
+
+#import <CoreWLAN/CoreWLAN.h>
+#import <CoreWLAN/CoreWLANConstants.h>
+#import <CoreWLAN/CWInterface.h>
+#import <CoreWLAN/CWWiFiClient.h>
+#import <CoreWLAN/CoreWLANTypes.h>
+
+namespace ndn {
+namespace ncc {
+
+const std::string adhocUri = "udp4://224.0.23.170:56363";
+const std::set<Name> adhocPrefixes = {"/", "/adhoc"};
+const NSUInteger g_channel = 11;
+const uint16_t ROUTE_ORIGIN_NCC = 67;
+
+Adhoc::Adhoc(Face& face, KeyChain& keychain)
+ : m_face(face)
+ , m_keyChain(keychain)
+ , m_controller(new nfd::Controller(m_face, m_keyChain))
+ , m_faceMonitor(new nfd::FaceMonitor(m_face))
+{
+ m_faceMonitor->onNotification.connect(bind(&Adhoc::onNotification, this, _1));
+}
+
+Adhoc::~Adhoc()
+{
+ delete m_faceMonitor;
+ delete m_controller;
+}
+
+bool
+Adhoc::createAdhoc()
+{
+ m_faceMonitor->start();
+
+ NSString* networkName =
+ [[NSString alloc] initWithCString:"NDNdirect" encoding:NSASCIIStringEncoding];
+ NSString* passphrase =
+ [[NSString alloc] initWithCString:"NDNhello" encoding:NSASCIIStringEncoding];
+ NSArray* airportInterfaces = [CWWiFiClient interfaceNames];
+ NSString* interfaceName = [airportInterfaces objectAtIndex:0];
+ CWWiFiClient* wifiInterfaces = [[CWWiFiClient alloc] init];
+ CWInterface* airport = [wifiInterfaces interfaceWithName:interfaceName];
+
+ NSError* error = nil;
+ NSData* data = [networkName dataUsingEncoding:NSUTF8StringEncoding];
+ BOOL created = [airport startIBSSModeWithSSID:data
+ security:kCWIBSSModeSecurityNone
+ channel:g_channel
+ password:passphrase
+ error:&error];
+
+ if (!created) {
+ return false;
+ }
+ else {
+ unregisterRoutes(ndn::nfd::ROUTE_ORIGIN_AUTOCONF);
+ return true;
+ }
+}
+
+void
+Adhoc::destroyAdhoc()
+{
+ unregisterRoutes(ROUTE_ORIGIN_NCC);
+ m_faceMonitor->stop();
+
+ NSArray* airportInterfaces = [CWWiFiClient interfaceNames];
+ NSString* interfaceName = [airportInterfaces objectAtIndex:0];
+ CWWiFiClient* wifiInterfaces = [[CWWiFiClient alloc] init];
+ CWInterface* airport = [wifiInterfaces interfaceWithName:interfaceName];
+ [airport disassociate];
+
+ NSError* err = nil;
+
+ [airport setPower:NO error:&err];
+ [NSThread sleepForTimeInterval: 3.0];
+ [airport setPower:YES error:&err];
+}
+
+void
+Adhoc::onNotification(const ndn::nfd::FaceEventNotification& notification)
+{
+ if (notification.getKind() == ndn::nfd::FACE_EVENT_CREATED &&
+ notification.getRemoteUri() == adhocUri &&
+ notification.getLinkType() == ndn::nfd::LINK_TYPE_MULTI_ACCESS) {
+ for (const auto& prefix : adhocPrefixes) {
+ ndn::nfd::ControlParameters params;
+ params
+ .setName(prefix)
+ .setFaceId(notification.getFaceId())
+ .setOrigin(ROUTE_ORIGIN_NCC);
+ m_controller->start<ndn::nfd::RibRegisterCommand>(params, nullptr, nullptr);
+ }
+ }
+}
+
+void
+Adhoc::unregisterRoutes(uint16_t origin)
+{
+ m_controller->fetch<ndn::nfd::RibDataset>([this, origin] (const std::vector<nfd::RibEntry>& ribs) {
+ for (const auto& rib : ribs) {
+ for (const auto& route : rib.getRoutes()) {
+ if (route.getOrigin() == origin) {
+ ndn::nfd::ControlParameters params;
+ params.setFaceId(route.getFaceId());
+ m_controller->start<ndn::nfd::FaceDestroyCommand>(params, nullptr, nullptr);
+ }
+ }
+ }
+ }, nullptr);
+}
+
+} // namespace ncc
+} // namespace ndn
+
+#endif // OSX_BUILD
diff --git a/src/qml.qrc b/src/qml.qrc
index a64901f..229d0fe 100644
--- a/src/qml.qrc
+++ b/src/qml.qrc
@@ -11,5 +11,6 @@
<file>../res/icon-connected-black.png</file>
<file>../res/icon-disconnected-black.png</file>
<file>../res/icon-connected-status-black.png</file>
+ <file>../res/icon-connected-adhoc-black.png</file>
</qresource>
</RCC>
diff --git a/src/tray-menu.cpp b/src/tray-menu.cpp
index de90134..cf3b5b8 100644
--- a/src/tray-menu.cpp
+++ b/src/tray-menu.cpp
@@ -31,6 +31,7 @@
#define CONNECT_ICON ":/res/icon-connected-black.png"
#define DISCONNECT_ICON ":/res/icon-disconnected-black.png"
#define CONNECT_STATUS_ICON ":/res/icon-connected-status-black.png"
+#define CONNECT_ADHOC_ICON ":/res/icon-connected-adhoc-black.png"
#include <Security/Authorization.h>
#include <Security/AuthorizationTags.h>
@@ -62,6 +63,7 @@
: m_context(context)
, m_isNfdRunning(false)
, m_isConnectedToHub(false)
+ , m_isConnectedToAdhoc(false)
, m_menu(new QMenu(this))
, m_entryPref(new QAction("Preferences...", m_menu))
, m_entryStatus(new QAction("Status...", m_menu))
@@ -70,6 +72,7 @@
, m_settings(new QSettings())
#ifdef OSX_BUILD
, m_entryEnableCli(new QAction("Install command-line tools...", m_menu))
+ , m_entryAdhoc(new QAction("Enable ad hoc Wi-Fi...", m_menu))
, m_checkForUpdates(new QAction("Check for updates", m_menu))
, m_sparkle(NCC_APPCAST)
#endif
@@ -77,6 +80,7 @@
, m_keyViewerDialog(new ncc::KeyViewerDialog)
, m_face(face)
, m_keyChain(keyChain)
+ , m_adhoc(m_face, m_keyChain)
, m_statusViewer(new StatusViewer(m_face, m_keyChain))
{
connect(m_entryPref, SIGNAL(triggered()), this, SIGNAL(showApp()));
@@ -84,10 +88,9 @@
connect(m_entrySec, SIGNAL(triggered()), m_keyViewerDialog, SLOT(present()));
connect(m_entryQuit, SIGNAL(triggered()), this, SLOT(quitApp()));
- connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)),
- Qt::QueuedConnection);
- connect(this, SIGNAL(connectivityUpdate(bool)), this, SLOT(updateConnectivity(bool)),
- Qt::QueuedConnection);
+ connect(this, SIGNAL(nfdActivityUpdate(bool)), this, SLOT(updateNfdActivityIcon(bool)), Qt::QueuedConnection);
+ connect(this, SIGNAL(connectivityUpdate(bool)), this, SLOT(updateConnectivity(bool)), Qt::QueuedConnection);
+ connect(this, SIGNAL(adhocUpdate(bool)), this, SLOT(updateAdhoc(bool)), Qt::QueuedConnection);
QString nccVersion = QString(NCC_VERSION) + " (ndn-cxx: " + NDN_CXX_VERSION_BUILD_STRING +
", NFD: " + NFD_VERSION_BUILD_STRING +
@@ -103,6 +106,11 @@
m_menu->addAction(m_entryPref);
#ifdef OSX_BUILD
+ m_entryAdhoc->setChecked(false);
+ m_entryAdhoc->setCheckable(true);
+ connect(m_entryAdhoc, SIGNAL(triggered()), this, SLOT(onAdhocChange()));
+ m_menu->addAction(m_entryAdhoc);
+
connect(m_entryEnableCli, SIGNAL(triggered()), this, SLOT(enableCli()));
m_menu->addAction(m_entryEnableCli);
@@ -314,6 +322,45 @@
m_settings->setValue("ENABLE_SHUTDOWN", isEnabled);
}
+///////////////////////////////////////////////////////////////////////////////
+// Ad hoc WiFi communication
+
+void
+TrayMenu::onAdhocChange()
+{
+#ifdef ADHOC_SUPPORTED
+ if (m_entryAdhoc->isChecked()) {
+ QMessageBox* msgBox = new QMessageBox();
+ msgBox->setText("WARNING: Wi-Fi will be disconnected!");
+ msgBox->setIcon(QMessageBox::Warning);
+ msgBox->setInformativeText(
+ "Are you sure that you are OK with that?");
+ msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+ msgBox->setDefaultButton(QMessageBox::No);
+ int ret = msgBox->exec();
+
+ if (ret == QMessageBox::No) {
+ m_entryAdhoc->setChecked(false);
+ }
+ else {
+ stopNdnAutoConfig();
+ m_entryAdhoc->setText("Adhoc Wi-Fi is enabled");
+ m_adhoc.createAdhoc();
+ std::cerr << "ad hoc is on!" << std::endl;
+ }
+ }
+ else {
+ m_entryAdhoc->setText("Enable Adhoc Wi-Fi...");
+ m_adhoc.destroyAdhoc();
+ std::cerr << "ad hos is off!" << std::endl;
+ // a trick in DestroyAdhoc ensures that WiFi will be reconnected to a default WiFi
+ if (isNdnAutoConfigEnabled()) {
+ startNdnAutoConfig();
+ }
+ }
+#endif
+}
+
////////////////////////////////////////////////////////////////////////////////
// Misc
@@ -361,13 +408,16 @@
m_isNfdRunning = isActive;
if (isActive) {
- if(m_isConnectedToHub) {
+ if (m_isConnectedToHub) {
m_tray->setIcon(QIcon(CONNECT_STATUS_ICON));
}
+ else if (m_isConnectedToAdhoc) {
+ m_tray->setIcon(QIcon(CONNECT_ADHOC_ICON));
+ }
else {
m_tray->setIcon(QIcon(CONNECT_ICON));
}
- if (isNdnAutoConfigEnabled()) {
+ if (!m_entryAdhoc->isChecked() && isNdnAutoConfigEnabled()) {
startNdnAutoConfig();
}
}
@@ -383,6 +433,12 @@
}
void
+TrayMenu::updateAdhoc(bool isConnectedToAdhoc)
+{
+ m_isConnectedToAdhoc = isConnectedToAdhoc;
+}
+
+void
TrayMenu::enableCli()
{
#ifdef OSX_BUILD
diff --git a/src/tray-menu.hpp b/src/tray-menu.hpp
index 1159a1d..d71804b 100644
--- a/src/tray-menu.hpp
+++ b/src/tray-menu.hpp
@@ -33,6 +33,7 @@
#include <QtWidgets/QSystemTrayIcon>
#include <QtWidgets/QAction>
#include <QtWidgets/QMenu>
+#include <QtWidgets/QMessageBox>
#include <QtQml/QQmlContext>
@@ -41,8 +42,11 @@
#ifdef OSX_BUILD
#include "osx-auto-update-sparkle.hpp"
+#include "osx-adhoc.hpp"
#endif // OSX_BUILD
+#include <thread>
+
namespace ndn {
class Face;
@@ -63,6 +67,9 @@
void
connectivityUpdate(bool isConnectedToHub);
+ void
+ adhocUpdate(bool isConnectedToAdhoc);
+
public:
explicit
TrayMenu(QQmlContext* context, Face& face, KeyChain& keyChain);
@@ -125,6 +132,9 @@
updateConnectivity(bool isConnectedToHub);
void
+ updateAdhoc(bool isConnectedToAdhoc);
+
+ void
enableCli();
void
@@ -135,6 +145,9 @@
#ifdef OSX_BUILD
void
+ onAdhocChange();
+
+ void
checkForUpdates();
#endif // OSX_BUILD
@@ -145,6 +158,7 @@
QQmlContext* m_context;
bool m_isNfdRunning;
bool m_isConnectedToHub;
+ bool m_isConnectedToAdhoc;
QSystemTrayIcon* m_tray;
QMenu* m_menu;
QAction* m_entryPref;
@@ -155,6 +169,7 @@
QString m_ndnAutoConfigMsg;
#ifdef OSX_BUILD
QAction* m_entryEnableCli;
+ QAction* m_entryAdhoc;
QAction* m_checkForUpdates;
OsxAutoUpdateSparkle m_sparkle;
#endif // OSX_BUILD
@@ -164,6 +179,7 @@
ncc::KeyViewerDialog* m_keyViewerDialog;
Face& m_face;
KeyChain& m_keyChain;
+ Adhoc m_adhoc;
StatusViewer* m_statusViewer;
};
diff --git a/wscript b/wscript
index 33f7684..18d8b78 100644
--- a/wscript
+++ b/wscript
@@ -1,5 +1,5 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
-VERSION='0.2.3'
+VERSION='0.2.4'
APPNAME='ndn-control-center'
APPCAST='https://named-data.net/binaries/NDN-Control-Center/ndn-control-center.xml'