Merge "buffet: Add a HACKING file"
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index 6b08e4c..d396525 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -35,9 +35,12 @@
       'sources': [
         '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',
       ],
@@ -57,6 +60,7 @@
       'type': 'executable',
       'sources': [
         'buffet_client.cc',
+        'dbus_constants.cc',
       ],
     },
     {
diff --git a/buffet/buffet_client.cc b/buffet/buffet_client.cc
index 83d5f9c..9568834 100755
--- a/buffet/buffet_client.cc
+++ b/buffet/buffet_client.cc
@@ -2,55 +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::kBuffetServiceName,
-      dbus::ObjectPath(buffet::kBuffetServicePath));
+      buffet::dbus_constants::kServiceName,
+      dbus::ObjectPath(object_path));
 }
 
-void CallTestMethod(dbus::ObjectProxy* proxy) {
+bool CallTestMethod(dbus::ObjectProxy* proxy) {
   int timeout_ms = 1000;
-  dbus::MethodCall method_call(buffet::kBuffetInterface,
-                               buffet::kTestMethod);
+  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
new file mode 100644
index 0000000..746058e
--- /dev/null
+++ b/buffet/dbus_constants.cc
@@ -0,0 +1,26 @@
+// 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/dbus_constants.h"
+
+namespace buffet {
+
+namespace dbus_constants {
+
+const char kServiceName[] = "org.chromium.Buffet";
+
+const char kRootInterface[] = "org.chromium.Buffet";
+const char kRootServicePath[] = "/org/chromium/Buffet";
+
+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
new file mode 100644
index 0000000..e30fdde
--- /dev/null
+++ b/buffet/dbus_constants.h
@@ -0,0 +1,34 @@
+// 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_CONSTANTS_H_
+#define BUFFET_DBUS_CONSTANTS_H_
+
+namespace buffet {
+
+namespace dbus_constants {
+
+// The service name claimed by the Buffet daemon.
+extern const char kServiceName[];
+
+// Interface implemented by the object at kRootServicePath.
+extern const char kRootInterface[];
+extern const char kRootServicePath[];
+
+// 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
+
+#endif  // BUFFET_DBUS_CONSTANTS_H_
diff --git a/buffet/dbus_manager.cc b/buffet/dbus_manager.cc
index c6031fc..ceddee1 100644
--- a/buffet/dbus_manager.cc
+++ b/buffet/dbus_manager.cc
@@ -8,6 +8,10 @@
 
 #include <base/bind.h>
 
+#include "buffet/dbus_constants.h"
+
+using ::std::string;
+
 namespace buffet {
 
 namespace {
@@ -29,8 +33,7 @@
 }  // namespace
 
 DBusManager::DBusManager()
-    : bus_(nullptr),
-      buffet_dbus_object_(nullptr) {}
+    : bus_(nullptr) {}
 
 DBusManager::~DBusManager() {}
 
@@ -49,26 +52,36 @@
   bus_ = new dbus::Bus(options);
   CHECK(bus_->Connect());
 
-  buffet_dbus_object_ = bus_->GetExportedObject(
-      dbus::ObjectPath(kBuffetServicePath));
-  ExportDBusMethod(kTestMethod, &DBusManager::HandleTestMethod);
+  // buffet_dbus_object is owned by the Bus.
+  auto buffet_dbus_object = GetExportedObject(dbus_constants::kRootServicePath);
+  ExportDBusMethod(
+      buffet_dbus_object,
+      dbus_constants::kRootInterface, dbus_constants::kRootTestMethod,
+      base::Bind(&DBusManager::HandleTestMethod, base::Unretained(this)));
 
-  CHECK(bus_->RequestOwnershipAndBlock(kBuffetServiceName,
+  CHECK(bus_->RequestOwnershipAndBlock(dbus_constants::kServiceName,
                                        dbus::Bus::REQUIRE_PRIMARY))
-      << "Unable to take ownership of " << kBuffetServiceName;
+      << "Unable to take ownership of " << dbus_constants::kServiceName;
 }
 
 void DBusManager::ShutDownDBus() {
   bus_->ShutdownAndBlock();
 }
 
-void DBusManager::ExportDBusMethod(const std::string& method_name,
-                              DBusMethodCallMemberFunction member) {
-  DCHECK(buffet_dbus_object_);
-  CHECK(buffet_dbus_object_->ExportMethodAndBlock(
-      kBuffetInterface, method_name,
-      base::Bind(&HandleSynchronousDBusMethodCall,
-                 base::Bind(member, base::Unretained(this)))));
+dbus::ExportedObject* DBusManager::GetExportedObject(
+    const string& object_path) {
+  return bus_->GetExportedObject(dbus::ObjectPath(object_path));
+}
+
+void DBusManager::ExportDBusMethod(
+    dbus::ExportedObject* exported_object,
+    const string& interface_name,
+    const string& method_name,
+    base::Callback<scoped_ptr<dbus::Response>(dbus::MethodCall*)> handler) {
+  DCHECK(exported_object);
+  CHECK(exported_object->ExportMethodAndBlock(
+      interface_name, method_name,
+      base::Bind(&HandleSynchronousDBusMethodCall, handler)));
 }
 
 scoped_ptr<dbus::Response> DBusManager::HandleTestMethod(
diff --git a/buffet/dbus_manager.h b/buffet/dbus_manager.h
index da91391..a990ead 100644
--- a/buffet/dbus_manager.h
+++ b/buffet/dbus_manager.h
@@ -14,21 +14,6 @@
 
 namespace buffet {
 
-// TODO(sosa): Move to chromeos/system_api once we're ready.
-const char kBuffetInterface[] = "org.chromium.Buffet";
-const char kBuffetServicePath[] = "/org/chromium/Buffet";
-const char kBuffetServiceName[] = "org.chromium.Buffet";
-
-// Methods exposed by buffet.
-const char kTestMethod[] = "TestMethod";
-
-class DBusManager;
-
-// Pointer to a member function for handling D-Bus method calls. If an empty
-// scoped_ptr is returned, an empty (but successful) response will be sent.
-typedef scoped_ptr<dbus::Response> (DBusManager::*DBusMethodCallMemberFunction)(
-    dbus::MethodCall*);
-
 // Class that manages dbus interactions in buffet.
 class DBusManager {
  public:
@@ -38,20 +23,30 @@
   void Init();
   void Finalize();
 
+  // Get an object owned by the ::dbus::Bus object.  This object
+  // has methods to export DBus facing methods.
+  ::dbus::ExportedObject* GetExportedObject(
+      const std::string& object_path);
+
+  // Exports |method_name| on |exported_object| and uses |member|
+  // to handle calls.
+  void ExportDBusMethod(
+      ::dbus::ExportedObject* exported_object,
+      const std::string& interface_name,
+      const std::string& method_name,
+      base::Callback<scoped_ptr<::dbus::Response>(
+          ::dbus::MethodCall*)> handler);
+
  private:
   // Connects to the D-Bus system bus and exports methods.
   void InitDBus();
   void ShutDownDBus();
 
-  // Exports |method_name| and uses |member| to handle calls.
-  void ExportDBusMethod(const std::string& method_name,
-                        DBusMethodCallMemberFunction member);
-
   // Callbacks for handling D-Bus signals and method calls.
-  scoped_ptr<dbus::Response> HandleTestMethod(dbus::MethodCall* method_call);
+  scoped_ptr<::dbus::Response> HandleTestMethod(
+      ::dbus::MethodCall* method_call);
 
-  scoped_refptr<dbus::Bus> bus_;
-  dbus::ExportedObject* buffet_dbus_object_;  // weak; owned by |bus_|
+  scoped_refptr<::dbus::Bus> bus_;
 
   DISALLOW_COPY_AND_ASSIGN(DBusManager);
 };
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_