lorgnette: Add startup-via-dbus

Add a DBus service entry which starts the lorgnette daemon process
when a DBus method call is made to its interface.  Add an idle timeout
which exits the daemon if no calls are received after an inactivity
timeout.

BUG=chromium:239087
TEST=emerge and reboot system

Change-Id: I6994b68f1fcf6adf652e993e0187ff66a612609c
Reviewed-on: https://gerrit.chromium.org/gerrit/56313
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Paul Stewart <pstew@chromium.org>
diff --git a/daemon.cc b/daemon.cc
index 852ba78..3b813ee 100644
--- a/daemon.cc
+++ b/daemon.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#include <base/bind.h>
 #include <base/logging.h>
 #include <base/message_loop_proxy.h>
 #include <base/run_loop.h>
@@ -22,6 +23,7 @@
 const char Daemon::kInterfaceName[] = "org.chromium.lorgnette";
 const char Daemon::kScanGroupName[] = "scanner";
 const char Daemon::kScanUserName[] = "saned";
+const int Daemon::kShutdownTimeoutMilliseconds = 20000;
 
 Daemon::Daemon()
     : dont_use_directly_(new MessageLoopForUI),
@@ -47,8 +49,18 @@
   dispatcher_->attach(NULL);
   connection_.reset(new DBus::Connection(DBus::Connection::SystemBus()));
   connection_->request_name(kInterfaceName);
-  manager_.reset(new Manager);
+  manager_.reset(new Manager(
+      base::Bind(&Daemon::PostponeShutdown, base::Unretained(this))));
   manager_->InitDBus(connection_.get());
+  PostponeShutdown();
+}
+
+void Daemon::PostponeShutdown() {
+  shutdown_callback_.Reset(base::Bind(&Daemon::Quit, base::Unretained(this)));
+  message_loop_proxy_->PostDelayedTask(FROM_HERE,
+                                       shutdown_callback_.callback(),
+                                       base::TimeDelta::FromMilliseconds(
+                                           kShutdownTimeoutMilliseconds));
 }
 
 }  // namespace lorgnette
diff --git a/daemon.h b/daemon.h
index ada5fb1..4e5e301 100644
--- a/daemon.h
+++ b/daemon.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include <base/cancelable_callback.h>
 #include <base/memory/scoped_ptr.h>
 #include <base/message_loop.h>
 #include <dbus-c++/glib-integration.h>
@@ -44,11 +45,18 @@
  private:
   friend class DaemonTest;
 
+  // Daemon will automatically shutdown after this length of idle time.
+  static const int kShutdownTimeoutMilliseconds;
+
+  // Restarts a timer for the termination of the daemon process.
+  void PostponeShutdown();
+
   scoped_ptr<MessageLoop> dont_use_directly_;
   scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
   scoped_ptr<DBus::Glib::BusDispatcher> dispatcher_;
   scoped_ptr<DBus::Connection> connection_;
   scoped_ptr<Manager> manager_;
+  base::CancelableClosure shutdown_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(Daemon);
 };
diff --git a/dbus_service/org.chromium.lorgnette.service b/dbus_service/org.chromium.lorgnette.service
new file mode 100644
index 0000000..db13365
--- /dev/null
+++ b/dbus_service/org.chromium.lorgnette.service
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=org.chromium.lorgnette
+Exec=/usr/bin/lorgnette
+User=root
+
diff --git a/manager.cc b/manager.cc
index 0c91553..547cce9 100644
--- a/manager.cc
+++ b/manager.cc
@@ -62,7 +62,8 @@
   manager_->ScanImage(device_name, outfd, scan_properties, &error);
 }
 
-Manager::Manager() {}
+Manager::Manager(base::Callback<void()> activity_callback)
+    : activity_callback_(activity_callback) {}
 
 Manager::~Manager() {}
 
@@ -91,6 +92,7 @@
     SetError(__func__, "Unable to read scanner list output file", error);
     return ScannerInfo();
   }
+  activity_callback_.Run();
   return ScannerInfoFromString(scanner_output_string);
 }
 
@@ -117,6 +119,7 @@
                       &scan_process,
                       &convert_process,
                       error);
+  activity_callback_.Run();
 }
 
 // static
diff --git a/manager.h b/manager.h
index bf88e8b..fd5f91c 100644
--- a/manager.h
+++ b/manager.h
@@ -29,7 +29,7 @@
   typedef std::map< std::string,
                     std::map<std::string, std::string> > ScannerInfo;
 
-  Manager();
+  Manager(base::Callback<void()> activity_callback);
   virtual ~Manager();
 
   // Start DBus connection.
@@ -114,6 +114,7 @@
                        ::DBus::Error *error);
 
   scoped_ptr<DBusAdaptor> dbus_adaptor_;
+  base::Callback<void()> activity_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(Manager);
 };
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 09948d0..3fe10a9 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -30,7 +30,8 @@
      : input_pipe_fd_(kInputPipeFd),
        output_pipe_fd_(kOutputPipeFd),
        input_scoped_fd_(&input_pipe_fd_),
-       output_scoped_fd_(&output_pipe_fd_) {}
+       output_scoped_fd_(&output_pipe_fd_),
+       manager_(base::Callback<void()>()) {}
 
   virtual void TearDown() {
     // The fds that we have handed to these ScopedFD are not real, so we