buffet: Add Manager class

This class implements the Manager DBus interface and is responsible for
global operations of Buffet.  For instance, this class is responsible
for initiating device regsitration and accepting state updates to be
published to the cloud.

BUG=chromium:355387
TEST=buffet_BasicDBusAPI passes.

Change-Id: Id38d9698048bd0fa722dc297a957c80e0a488870
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index 8c62edd..d396525 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -36,9 +36,11 @@
         'data_encoding.cc',
         'dbus_manager.cc',
         'dbus_constants.cc',
+        'dbus_utils.cc',
         'http_request.cc',
         'http_transport_curl.cc',
         'http_utils.cc',
+        'manager.cc',
         'mime_utils.cc',
         'string_utils.cc',
       ],
diff --git a/buffet/buffet_client.cc b/buffet/buffet_client.cc
index f7072ff..9568834 100755
--- a/buffet/buffet_client.cc
+++ b/buffet/buffet_client.cc
@@ -2,56 +2,148 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <iostream>
 #include <string>
 
 #include <base/logging.h>
 #include <base/memory/scoped_ptr.h>
 #include <dbus/bus.h>
 #include <dbus/object_proxy.h>
-#include <gflags/gflags.h>
+#include <dbus/message.h>
 
 #include "buffet/dbus_constants.h"
 #include "buffet/dbus_manager.h"
 
-DEFINE_bool(testmethod, false, "Call the Buffet Test Method.");
+using namespace buffet::dbus_constants;
 
 namespace {
 
-dbus::ObjectProxy* GetBuffetDBusProxy(dbus::Bus *bus) {
+dbus::ObjectProxy* GetBuffetDBusProxy(dbus::Bus *bus,
+                                      const std::string& object_path) {
   return bus->GetObjectProxy(
       buffet::dbus_constants::kServiceName,
-      dbus::ObjectPath(buffet::dbus_constants::kRootServicePath));
+      dbus::ObjectPath(object_path));
 }
 
-void CallTestMethod(dbus::ObjectProxy* proxy) {
+bool CallTestMethod(dbus::ObjectProxy* proxy) {
   int timeout_ms = 1000;
   dbus::MethodCall method_call(buffet::dbus_constants::kRootInterface,
                                buffet::dbus_constants::kRootTestMethod);
   scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call,
                                                                 timeout_ms));
   if (!response) {
-    LOG(ERROR) << "Failed to receive a response.";
-    return;
-  } else {
-    LOG(INFO) << "Received a response.";
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
   }
+  std::cout << "Received a response." << std::endl;
+  return true;
 }
 
-} // end namespace
-
-int main(int argc, char** argv) {
-  google::ParseCommandLineFlags(&argc, &argv, true);
-
-  dbus::Bus::Options options;
-  options.bus_type = dbus::Bus::SYSTEM;
-  scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
-
-  auto proxy = GetBuffetDBusProxy(bus);
-  if (FLAGS_testmethod) {
-    CallTestMethod(proxy);
+bool CallManagerRegisterDevice(dbus::ObjectProxy* proxy,
+                               const std::string& client_id,
+                               const std::string& client_secret,
+                               const std::string& api_key) {
+  dbus::MethodCall method_call(
+      buffet::dbus_constants::kManagerInterface,
+      buffet::dbus_constants::kManagerRegisterDeviceMethod);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(client_id);
+  writer.AppendString(client_secret);
+  writer.AppendString(api_key);
+  int timeout_ms = 1000;
+  scoped_ptr<dbus::Response> response(
+      proxy->CallMethodAndBlock(&method_call, timeout_ms));
+  if (!response) {
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
   }
 
-  LOG(INFO) << "Done.";
-  return 0;
+  dbus::MessageReader reader(response.get());
+  std::string registration_id;
+  if (!reader.PopString(&registration_id)) {
+    std::cout << "No registration id in response." << std::endl;
+    return false;
+  }
+
+  std::cout << "Registration ID is " << registration_id << std::endl;
+  return true;
+}
+
+bool CallManagerUpdateState(dbus::ObjectProxy* proxy,
+                            const std::string& json_blob) {
+  dbus::MethodCall method_call(
+      buffet::dbus_constants::kManagerInterface,
+      buffet::dbus_constants::kManagerUpdateStateMethod);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(json_blob);
+  int timeout_ms = 1000;
+  scoped_ptr<dbus::Response> response(
+      proxy->CallMethodAndBlock(&method_call, timeout_ms));
+  if (!response) {
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
+  }
+  return true;
+}
+
+void usage() {
+  std::cerr << "Possible commands:" << std::endl;
+  std::cerr << "  " << kRootTestMethod << std::endl;
+  std::cerr << "  " << kManagerRegisterDeviceMethod
+            << "  " << " <client id> <client secret> <api key>" << std::endl;
+  std::cerr << "  " << kManagerUpdateStateMethod << std::endl;
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+  dbus::Bus::Options options;
+  options.bus_type = dbus::Bus::SYSTEM;
+  scoped_refptr<dbus::Bus> bus(new dbus::Bus(options));
+
+  if (argc < 2) {
+    usage();
+    return -1;
+  }
+
+  char* command = argv[1];
+  bool success = false;
+  if (strcmp(command, kRootTestMethod) == 0) {
+    auto proxy = GetBuffetDBusProxy(
+        bus, buffet::dbus_constants::kRootServicePath);
+    success = CallTestMethod(proxy);
+  } else if (strcmp(command, kManagerRegisterDeviceMethod) == 0) {
+    if (argc != 5) {
+      std::cerr << "Invalid number of arguments for "
+                << "Manager.RegisterDevice" << std::endl;
+      usage();
+      return -1;
+    }
+    auto proxy = GetBuffetDBusProxy(
+        bus, buffet::dbus_constants::kManagerServicePath);
+    success = CallManagerRegisterDevice(proxy, argv[2], argv[3], argv[4]);
+  } else if (strcmp(command, kManagerUpdateStateMethod) == 0) {
+    if (argc != 3) {
+      std::cerr << "Invalid number of arguments for "
+                << "Manager.UpdateState" << std::endl;
+      usage();
+      return -1;
+    }
+    auto proxy = GetBuffetDBusProxy(
+        bus, buffet::dbus_constants::kManagerServicePath);
+    success = CallManagerUpdateState(proxy, argv[2]);
+  } else {
+    std::cerr << "Unkown command: " << command << std::endl;
+    usage();
+    return -1;
+  }
+
+  if (success) {
+    std::cout << "Done." << std::endl;
+    return 0;
+  }
+
+  std::cout << "Done, with errors." << std::endl;
+  return -1;
 }
 
diff --git a/buffet/dbus_constants.cc b/buffet/dbus_constants.cc
index 701f387..746058e 100644
--- a/buffet/dbus_constants.cc
+++ b/buffet/dbus_constants.cc
@@ -15,6 +15,12 @@
 
 const char kRootTestMethod[] = "TestMethod";
 
+const char kManagerInterface[] = "org.chromium.Buffet.Manager";
+const char kManagerServicePath[] = "/org/chromium/Buffet/Manager";
+
+const char kManagerUpdateStateMethod[] = "UpdateState";
+const char kManagerRegisterDeviceMethod[] = "RegisterDevice";
+
 }  // namespace dbus_constants
 
 }  // namespace buffet
diff --git a/buffet/dbus_constants.h b/buffet/dbus_constants.h
index 1317ab4..e30fdde 100644
--- a/buffet/dbus_constants.h
+++ b/buffet/dbus_constants.h
@@ -19,6 +19,14 @@
 // Methods exposed as part of kRootInterface.
 extern const char kRootTestMethod[];
 
+// Interface implemented by the object at kManagerServicePath.
+extern const char kManagerInterface[];
+extern const char kManagerServicePath[];
+
+// Methods exposed as part of kManagerInterface.
+extern const char kManagerUpdateStateMethod[];
+extern const char kManagerRegisterDeviceMethod[];
+
 }  // namespace dbus_constants
 
 }  // namespace buffet
diff --git a/buffet/dbus_utils.cc b/buffet/dbus_utils.cc
new file mode 100644
index 0000000..fcda628
--- /dev/null
+++ b/buffet/dbus_utils.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <base/logging.h>
+
+#include "buffet/dbus_utils.h"
+
+namespace buffet {
+
+namespace dbus_utils {
+
+scoped_ptr<dbus::Response> GetBadArgsError(dbus::MethodCall* method_call,
+                                           const std::string& message) {
+  LOG(ERROR) << "Error while handling DBus call: " << message;
+  scoped_ptr<dbus::ErrorResponse> resp(dbus::ErrorResponse::FromMethodCall(
+      method_call, "org.freedesktop.DBus.Error.InvalidArgs", message));
+  return scoped_ptr<dbus::Response>(resp.release());
+}
+
+}  // namespace dbus_utils
+
+}  // namespace buffet
diff --git a/buffet/dbus_utils.h b/buffet/dbus_utils.h
new file mode 100644
index 0000000..dea6d02
--- /dev/null
+++ b/buffet/dbus_utils.h
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BUFFET_DBUS_UTILS_H_
+#define BUFFET_DBUS_UTILS_H_
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+#include <dbus/message.h>
+
+namespace buffet {
+
+namespace dbus_utils {
+
+scoped_ptr<dbus::Response> GetBadArgsError(dbus::MethodCall* method_call,
+                                           const std::string& message);
+
+}  // namespace dbus_utils
+
+}  // namespace buffet
+
+#endif  // BUFFET_DBUS_UTILS_H_
+
diff --git a/buffet/main.cc b/buffet/main.cc
index 0b2e493..79843ab 100644
--- a/buffet/main.cc
+++ b/buffet/main.cc
@@ -15,6 +15,7 @@
 #include <gflags/gflags.h>
 
 #include "buffet/dbus_manager.h"
+#include "buffet/manager.h"
 
 DEFINE_string(logsroot, "/var/log", "Root directory for buffet logs.");
 
@@ -79,8 +80,11 @@
   // Initialize the dbus_manager.
   buffet::DBusManager dbus_manager;
   dbus_manager.Init();
-
-  message_loop.Run();
+  {
+    // The Manager needs the dbus_manager to remain in scope for its lifetime.
+    buffet::Manager manager(&dbus_manager);
+    message_loop.Run();
+  }
 
   dbus_manager.Finalize();
   return 0;
diff --git a/buffet/manager.cc b/buffet/manager.cc
new file mode 100644
index 0000000..4daa9fc
--- /dev/null
+++ b/buffet/manager.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "buffet/manager.h"
+
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+
+#include "buffet/dbus_constants.h"
+#include "buffet/dbus_manager.h"
+#include "buffet/dbus_utils.h"
+
+using buffet::dbus_utils::GetBadArgsError;
+
+namespace buffet {
+
+Manager::Manager(DBusManager* dbus_manager) : dbus_manager_(dbus_manager) {
+  dbus::ExportedObject* exported_object = dbus_manager_->GetExportedObject(
+      dbus_constants::kManagerServicePath);
+  dbus_manager_->ExportDBusMethod(exported_object,
+                                  dbus_constants::kManagerInterface,
+                                  dbus_constants::kManagerRegisterDeviceMethod,
+                                  base::Bind(&Manager::HandleRegisterDevice,
+                                             base::Unretained(this)));
+  dbus_manager_->ExportDBusMethod(exported_object,
+                                  dbus_constants::kManagerInterface,
+                                  dbus_constants::kManagerUpdateStateMethod,
+                                  base::Bind(&Manager::HandleUpdateState,
+                                             base::Unretained(this)));
+}
+
+Manager::~Manager() {
+  // Unregister ourselves from the Bus.  This prevents the bus from calling
+  // our callbacks in between the Manager's death and the bus unregistering
+  // our exported object on shutdown.  Unretained makes no promises of memory
+  // management.
+  auto exported_object = dbus_manager_->GetExportedObject(
+      dbus_constants::kManagerServicePath);
+  exported_object->Unregister();
+}
+
+scoped_ptr<dbus::Response> Manager::HandleRegisterDevice(
+    dbus::MethodCall* method_call) {
+  // Read the parameters to the method.
+  dbus::MessageReader reader(method_call);
+  if (!reader.HasMoreData()) {
+    return GetBadArgsError(method_call, "No parameters to RegisterDevice");
+  }
+  std::string client_id, client_secret, api_key;
+  if (!reader.PopString(&client_id)) {
+    return GetBadArgsError(method_call, "Failed to read client_id");
+  }
+  if (!reader.PopString(&client_secret)) {
+    return GetBadArgsError(method_call, "Failed to read client_secret");
+  }
+  if (!reader.PopString(&api_key)) {
+    return GetBadArgsError(method_call, "Failed to read api_key");
+  }
+  if (reader.HasMoreData()) {
+    return GetBadArgsError(
+        method_call, "Too many parameters to RegisterDevice");
+  }
+
+  LOG(INFO) << "Received call to Manager.RegisterDevice()";
+  // TODO(wiley): Do something with these parameters to register the device.
+
+  // Send back our response.
+  scoped_ptr<dbus::Response> response(
+      dbus::Response::FromMethodCall(method_call));
+  dbus::MessageWriter writer(response.get());
+  writer.AppendString("<registration ticket id>");
+  return response.Pass();
+}
+
+scoped_ptr<dbus::Response> Manager::HandleUpdateState(
+    dbus::MethodCall *method_call) {
+  // Read the parameters to the method.
+  dbus::MessageReader reader(method_call);
+  if (!reader.HasMoreData()) {
+    return GetBadArgsError(method_call, "No parameters to UpdateState");
+  }
+  std::string json_state_fragment;
+  if (!reader.PopString(&json_state_fragment)) {
+    return GetBadArgsError(method_call, "Failed to read json_state_fragment");
+  }
+  if (reader.HasMoreData()) {
+    return GetBadArgsError(method_call, "Too many parameters to UpdateState");
+  }
+
+  LOG(INFO) << "Received call to Manager.UpdateState()";
+  // TODO(wiley): Do something with these parameters to update state.
+
+  // Send back our response.
+  return dbus::Response::FromMethodCall(method_call);
+}
+
+}  // namespace buffet
diff --git a/buffet/manager.h b/buffet/manager.h
new file mode 100644
index 0000000..d373a5e
--- /dev/null
+++ b/buffet/manager.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BUFFET_MANAGER_H_
+#define BUFFET_MANAGER_H_
+
+#include <base/basictypes.h>
+#include <base/memory/scoped_ptr.h>
+#include <dbus/message.h>
+
+namespace buffet {
+
+class DBusManager;
+
+// The Manager is responsible for global state of Buffet.  It exposes
+// interfaces which affect the entire device such as device registration and
+// device state.
+class Manager {
+ public:
+  Manager(DBusManager* dbus_manager);
+  ~Manager();
+
+ private:
+  // Handles calls to org.chromium.Buffet.Manager.RegisterDevice().
+  scoped_ptr<dbus::Response> HandleRegisterDevice(
+      dbus::MethodCall* method_call);
+  // Handles calls to org.chromium.Buffet.Manager.UpdateState().
+  scoped_ptr<dbus::Response> HandleUpdateState(
+      dbus::MethodCall* method_call);
+
+  DBusManager* dbus_manager_;  // Weak;  DBusManager should outlive Manager.
+
+  DISALLOW_COPY_AND_ASSIGN(Manager);
+};
+
+}  // namespace buffet
+
+#endif  // BUFFET_MANAGER_H_