util: Out-of-the-box support for Android logging

Refs: #4970
Change-Id: I01cbb59faf505046c792393eb2e8df347212abcb
diff --git a/.waf-tools/cross.py b/.waf-tools/cross.py
new file mode 100644
index 0000000..17b8c8f
--- /dev/null
+++ b/.waf-tools/cross.py
@@ -0,0 +1,25 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+from waflib import Utils
+
+def options(opt):
+    opt.add_option('--build', default=Utils.unversioned_sys_platform(),
+                   help='Build platform that is doing the actual compilation (e.g., darwin)')
+    opt.add_option('--host', default=Utils.unversioned_sys_platform(),
+                   help='Host platform on which the compiled binary will run (e.g., android)')
+    # opt.add_option('--target', default=Utils.unversioned_sys_platform(),
+    #                help='Target platform on which the compiled binary's output will run')
+
+def configure(conf):
+    conf.env.BUILD = conf.options.build
+    conf.env.HOST = conf.options.host
+    # conf.env.TARGET = conf.options.target
+
+    conf.start_msg('Build platform')
+    conf.end_msg(conf.env.BUILD)
+
+    conf.start_msg('Host platform')
+    conf.end_msg(conf.env.HOST)
+
+    # conf.start_msg('Target platform')
+    # conf.end_msg(conf.env.TARGET)
diff --git a/ndn-cxx/util/impl/logger-android.cpp b/ndn-cxx/util/impl/logger-android.cpp
new file mode 100644
index 0000000..bcfeef5
--- /dev/null
+++ b/ndn-cxx/util/impl/logger-android.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2021 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "ndn-cxx/util/impl/logger-android.hpp"
+
+#include <boost/log/sinks.hpp>
+
+#include <android/log.h>
+
+namespace ndn {
+namespace util {
+namespace detail {
+
+class AndroidSinkBackend : public boost::log::sinks::basic_sink_backend<boost::log::sinks::concurrent_feeding>
+{
+public:
+  static int
+  convertToAndroidSeverity(LogLevel level)
+  {
+    switch (level) {
+    case LogLevel::FATAL:
+      return ANDROID_LOG_FATAL;
+    case LogLevel::ERROR:
+      return ANDROID_LOG_ERROR;
+    case LogLevel::WARN:
+      return ANDROID_LOG_WARN;
+    case LogLevel::INFO:
+      return ANDROID_LOG_INFO;
+    case LogLevel::DEBUG:
+      return ANDROID_LOG_DEBUG;
+    case LogLevel::TRACE:
+      return ANDROID_LOG_VERBOSE;
+    case LogLevel::NONE: // not a real log level, but just for translation
+      return ANDROID_LOG_SILENT;
+    case LogLevel::ALL:
+      return ANDROID_LOG_VERBOSE; // this is "ALL" for Android
+    }
+  }
+
+  void
+  consume(const boost::log::record_view& rec)
+  {
+    auto severity = convertToAndroidSeverity(rec[log::severity].get());
+    auto module = rec[log::module].get();
+    auto msg = rec[boost::log::expressions::smessage].get();
+
+    __android_log_write(severity, module.data(), msg.data());
+  }
+};
+
+boost::shared_ptr<boost::log::sinks::sink>
+makeAndroidLogger()
+{
+  return boost::make_shared<boost::log::sinks::synchronous_sink<AndroidSinkBackend>>();
+}
+
+} // namespace detail
+} // namespace util
+} // namespace ndn
diff --git a/ndn-cxx/util/impl/logger-android.hpp b/ndn-cxx/util/impl/logger-android.hpp
new file mode 100644
index 0000000..e31402d
--- /dev/null
+++ b/ndn-cxx/util/impl/logger-android.hpp
@@ -0,0 +1,42 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2021 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_CXX_UTIL_IMPL_LOGGER_ANDROID_HPP
+#define NDN_CXX_UTIL_IMPL_LOGGER_ANDROID_HPP
+
+#include "ndn-cxx/util/logger.hpp"
+
+#ifndef __ANDROID__
+#error "This file should not be compiled ..."
+#endif
+
+namespace ndn {
+namespace util {
+namespace detail {
+
+boost::shared_ptr<boost::log::sinks::sink>
+makeAndroidLogger();
+
+} // namespace detail
+} // namespace util
+} // namespace ndn
+
+#endif // NDN_CXX_UTIL_IMPL_LOGGER_ANDROID_HPP
diff --git a/ndn-cxx/util/logging.cpp b/ndn-cxx/util/logging.cpp
index 4db9f28..77654fb 100644
--- a/ndn-cxx/util/logging.cpp
+++ b/ndn-cxx/util/logging.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -23,6 +23,10 @@
 #include "ndn-cxx/util/logger.hpp"
 #include "ndn-cxx/util/time.hpp"
 
+#ifdef __ANDROID__
+#include "ndn-cxx/util/impl/logger-android.hpp"
+#endif
+
 #include <boost/log/attributes/function.hpp>
 #include <boost/log/expressions.hpp>
 #include <boost/log/expressions/attr.hpp>
@@ -90,14 +94,19 @@
 
 Logging::Logging()
 {
+  // cannot call the static setDestination, as the singleton object is not yet constructed
+#ifndef __ANDROID__
   bool wantAutoFlush = true;
   const char* environ = std::getenv("NDN_LOG_NOFLUSH");
   if (environ != nullptr) {
     wantAutoFlush = false;
   }
 
-  // cannot call the static setDestination that uses the singleton Logging object that is not yet constructed
-  auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&std::clog, [] (auto) {}), wantAutoFlush);
+  auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&std::clog, [] (auto) {}),
+                                                  wantAutoFlush);
+#else
+  auto destination = detail::makeAndroidLogger();
+#endif // __ANDROID__
   this->setDestinationImpl(std::move(destination));
 
   environ = std::getenv("NDN_LOG");
@@ -105,7 +114,8 @@
     this->setLevelImpl(environ);
   }
 
-  boost::log::core::get()->add_global_attribute("Timestamp", boost::log::attributes::make_function(&log::makeTimestamp));
+  boost::log::core::get()->add_global_attribute("Timestamp",
+                                                boost::log::attributes::make_function(&log::makeTimestamp));
 }
 
 void
diff --git a/ndn-cxx/util/logging.hpp b/ndn-cxx/util/logging.hpp
index 71e170e..0019569 100644
--- a/ndn-cxx/util/logging.hpp
+++ b/ndn-cxx/util/logging.hpp
@@ -103,7 +103,7 @@
    *
    */
   static void
-  setDestination(std::ostream& os, bool wantAutoFlush = true);
+  setDestination(std::ostream& os, bool wantAutoFlush);
 
   /** \brief Flush log backend.
    *
diff --git a/tests/unit/util/logging.t.cpp b/tests/unit/util/logging.t.cpp
index 24ae88b..7daa1cd 100644
--- a/tests/unit/util/logging.t.cpp
+++ b/tests/unit/util/logging.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -178,7 +178,7 @@
   {
     m_systemClock->setNow(LOG_SYSTIME);
     Logging::get().resetLevels();
-    Logging::setDestination(os);
+    Logging::setDestination(os, true);
   }
 
   ~LoggingFixture()
@@ -689,7 +689,7 @@
     ));
 
   os2.reset();
-  Logging::setDestination(os);
+  Logging::setDestination(os, true);
   BOOST_CHECK(os2weak.expired());
 }
 
diff --git a/wscript b/wscript
index fc739f8..cad98bc 100644
--- a/wscript
+++ b/wscript
@@ -11,7 +11,7 @@
 
 def options(opt):
     opt.load(['compiler_cxx', 'gnu_dirs', 'c_osx'])
-    opt.load(['default-compiler-flags', 'compiler-features',
+    opt.load(['cross', 'default-compiler-flags', 'compiler-features',
               'coverage', 'pch', 'sanitizers', 'osx-frameworks',
               'boost', 'openssl', 'sqlite3',
               'doxygen', 'sphinx_build'],
@@ -71,7 +71,7 @@
     if not conf.options.enable_shared and not conf.options.enable_static:
         conf.fatal('Either static library or shared library must be enabled')
 
-    conf.load(['compiler_cxx', 'gnu_dirs', 'c_osx',
+    conf.load(['cross', 'compiler_cxx', 'gnu_dirs', 'c_osx',
                'default-compiler-flags', 'compiler-features',
                'pch', 'osx-frameworks', 'boost', 'openssl', 'sqlite3',
                'doxygen', 'sphinx_build'])
@@ -190,9 +190,10 @@
     libndn_cxx = dict(
         target='ndn-cxx',
         source=bld.path.ant_glob('ndn-cxx/**/*.cpp',
-                                 excl=['ndn-cxx/**/*-osx.cpp',
-                                       'ndn-cxx/**/*netlink*.cpp',
-                                       'ndn-cxx/**/*-sqlite3.cpp']),
+                                 excl=['ndn-cxx/**/*-android.cpp',
+                                       'ndn-cxx/**/*-osx.cpp',
+                                       'ndn-cxx/**/*-sqlite3.cpp',
+                                       'ndn-cxx/**/*netlink*.cpp']),
         features='pch',
         headers='ndn-cxx/impl/common-pch.hpp',
         use='ndn-cxx-mm-objects version BOOST OPENSSL SQLITE3 ATOMIC RT PTHREAD',
@@ -200,16 +201,19 @@
         export_includes='.',
         install_path='${LIBDIR}')
 
+    if bld.env.HOST == 'android':
+        libndn_cxx['source'] += bld.path.ant_glob('ndn-cxx/**/*-android.cpp')
+
     if bld.env.HAVE_OSX_FRAMEWORKS:
         libndn_cxx['source'] += bld.path.ant_glob('ndn-cxx/**/*-osx.cpp')
         libndn_cxx['use'] += ' OSX_COREFOUNDATION OSX_SECURITY OSX_SYSTEMCONFIGURATION OSX_FOUNDATION OSX_COREWLAN'
 
-    if bld.env.HAVE_NETLINK:
-        libndn_cxx['source'] += bld.path.ant_glob('ndn-cxx/**/*netlink*.cpp')
-
     # In case we want to make it optional later
     libndn_cxx['source'] += bld.path.ant_glob('ndn-cxx/**/*-sqlite3.cpp')
 
+    if bld.env.HAVE_NETLINK:
+        libndn_cxx['source'] += bld.path.ant_glob('ndn-cxx/**/*netlink*.cpp')
+
     if bld.env.enable_shared:
         bld.shlib(name='ndn-cxx',
                   vnum=VERSION_BASE,
@@ -274,20 +278,24 @@
         bld.recurse('examples')
 
     headers = bld.path.ant_glob('ndn-cxx/**/*.hpp',
-                                excl=['ndn-cxx/**/*-osx.hpp',
-                                      'ndn-cxx/**/*netlink*.hpp',
+                                excl=['ndn-cxx/**/*-android.hpp',
+                                      'ndn-cxx/**/*-osx.hpp',
                                       'ndn-cxx/**/*-sqlite3.hpp',
+                                      'ndn-cxx/**/*netlink*.hpp',
                                       'ndn-cxx/**/impl/**/*'])
 
+    if bld.env.HOST == 'android':
+        headers += bld.path.ant_glob('ndn-cxx/**/*-android.hpp', excl='ndn-cxx/**/impl/**/*')
+
     if bld.env.HAVE_OSX_FRAMEWORKS:
         headers += bld.path.ant_glob('ndn-cxx/**/*-osx.hpp', excl='ndn-cxx/**/impl/**/*')
 
-    if bld.env.HAVE_NETLINK:
-        headers += bld.path.ant_glob('ndn-cxx/**/*netlink*.hpp', excl='ndn-cxx/**/impl/**/*')
-
     # In case we want to make it optional later
     headers += bld.path.ant_glob('ndn-cxx/**/*-sqlite3.hpp', excl='ndn-cxx/**/impl/**/*')
 
+    if bld.env.HAVE_NETLINK:
+        headers += bld.path.ant_glob('ndn-cxx/**/*netlink*.hpp', excl='ndn-cxx/**/impl/**/*')
+
     bld.install_files(bld.env.INCLUDEDIR, headers, relative_trick=True)
 
     # Install generated headers