Add support for generic internal error reporting

Currently, we're using brillo::Error for internal error reporting.
However, brillo::Error is more tailored for D-Bus.

Instead, implement a new Error class (trimmed down version of
shill::Error) for internal error reporting, and provide
functions for converting it to RPC specific error object.

Bug: None
TEST=Run newly added unit tests

Change-Id: I2b518fae6eccb8dc9f9f0e96b941289c3846a9af
diff --git a/Android.mk b/Android.mk
index d2d0d00..c13c1ae 100644
--- a/Android.mk
+++ b/Android.mk
@@ -76,6 +76,7 @@
     device_info.cc \
     dhcp_server.cc \
     dhcp_server_factory.cc \
+    error.cc \
     event_dispatcher.cc \
     file_writer.cc \
     firewall_manager.cc \
@@ -109,6 +110,7 @@
     device_info_unittest.cc \
     device_unittest.cc \
     dhcp_server_unittest.cc \
+    error_unittest.cc \
     fake_device_adaptor.cc \
     hostapd_monitor_unittest.cc \
     manager_unittest.cc \
diff --git a/apmanager.gyp b/apmanager.gyp
index 755e63f..4ce5251 100644
--- a/apmanager.gyp
+++ b/apmanager.gyp
@@ -84,6 +84,7 @@
         'device_info.cc',
         'dhcp_server.cc',
         'dhcp_server_factory.cc',
+        'error.cc',
         'event_dispatcher.cc',
         'file_writer.cc',
         'firewall_manager.cc',
@@ -145,6 +146,7 @@
             'device_info_unittest.cc',
             'device_unittest.cc',
             'dhcp_server_unittest.cc',
+            'error_unittest.cc',
             'fake_device_adaptor.cc',
             'hostapd_monitor_unittest.cc',
             'manager_unittest.cc',
diff --git a/error.cc b/error.cc
new file mode 100644
index 0000000..6745efa
--- /dev/null
+++ b/error.cc
@@ -0,0 +1,86 @@
+//
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "apmanager/error.h"
+
+#include <base/files/file_path.h>
+#include <base/logging.h>
+#include <brillo/errors/error.h>
+#include <brillo/errors/error_codes.h>
+
+#if defined(__ANDROID__)
+#include <dbus/service_constants.h>
+#else
+#include <chromeos/dbus/service_constants.h>
+#endif  // __ANDROID__
+
+using std::string;
+
+namespace apmanager {
+
+Error::Error() : type_(kSuccess) {}
+
+Error::~Error() {}
+
+void Error::Populate(Type type,
+                     const string& message,
+                     const tracked_objects::Location& location) {
+  CHECK(type < kNumErrors) << "Error type out of range: " << type;
+  type_ = type;
+  message_ = message;
+  location_ = location;
+}
+
+void Error::Reset() {
+  type_ = kSuccess;
+  message_ = "";
+  location_ = tracked_objects::Location();
+}
+
+bool Error::ToDBusError(brillo::ErrorPtr* error) const {
+  if (IsSuccess()) {
+    return false;
+  }
+
+  string error_code = kErrorInternalError;
+  if (type_ == kInvalidArguments) {
+    error_code = kErrorInvalidArguments;
+  } else if (type_ == kInvalidConfiguration) {
+    error_code = kErrorInvalidConfiguration;
+  }
+
+  brillo::Error::AddTo(error,
+                       location_,
+                       brillo::errors::dbus::kDomain,
+                       error_code,
+                       message_);
+  return true;
+}
+
+// static
+void Error::PopulateAndLog(Error* error,
+                           Type type,
+                           const string& message,
+                           const tracked_objects::Location& from_here) {
+  string file_name = base::FilePath(from_here.file_name()).BaseName().value();
+  LOG(ERROR) << "[" << file_name << "("
+             << from_here.line_number() << ")]: "<< message;
+  if (error) {
+    error->Populate(type, message, from_here);
+  }
+}
+
+}  // namespace apmanager
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..304b25d
--- /dev/null
+++ b/error.h
@@ -0,0 +1,84 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef APMANAGER_ERROR_H_
+#define APMANAGER_ERROR_H_
+
+#include <memory>
+#include <string>
+
+#include <base/location.h>
+#include <base/macros.h>
+
+namespace brillo {
+class Error;
+using ErrorPtr = std::unique_ptr<Error>;
+}  // namespace brillo
+
+namespace apmanager {
+
+class Error {
+ public:
+  enum Type {
+    kSuccess = 0,  // No error.
+    kOperationInProgress,
+    kInternalError,
+    kInvalidArguments,
+    kInvalidConfiguration,
+    kNumErrors
+  };
+
+  Error();
+  ~Error();
+
+  void Populate(Type type,
+                const std::string& message,
+                const tracked_objects::Location& location);
+
+  void Reset();
+
+  Type type() const { return type_; }
+  const std::string& message() const { return message_; }
+
+  bool IsSuccess() const { return type_ == kSuccess; }
+  bool IsFailure() const { return !IsSuccess() && !IsOngoing(); }
+  bool IsOngoing() const { return type_ == kOperationInProgress; }
+
+  // Log an error message from |from_here|.  If |error| is non-NULL, also
+  // populate it.
+  static void PopulateAndLog(Error* error,
+                             Type type,
+                             const std::string& message,
+                             const tracked_objects::Location& from_here);
+
+  // TODO(zqiu): put this under a compiler flag (e.g. __DBUS__).
+  // Sets the D-Bus error and returns true if Error represents failure.
+  // Leaves error unchanged, and returns false otherwise.
+  bool ToDBusError(brillo::ErrorPtr* error) const;
+
+ private:
+  friend class ErrorTest;
+
+  Type type_;
+  std::string message_;
+  tracked_objects::Location location_;
+
+  DISALLOW_COPY_AND_ASSIGN(Error);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_ERROR_H_
diff --git a/error_unittest.cc b/error_unittest.cc
new file mode 100644
index 0000000..7e51c08
--- /dev/null
+++ b/error_unittest.cc
@@ -0,0 +1,88 @@
+//
+// Copyright (C) 2011 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "apmanager/error.h"
+
+#include <string>
+
+#include <brillo/errors/error.h>
+#include <brillo/errors/error_codes.h>
+#include <gtest/gtest.h>
+
+#if defined(__ANDROID__)
+#include <dbus/service_constants.h>
+#else
+#include <chromeos/dbus/service_constants.h>
+#endif  // __ANDROID__
+
+using std::string;
+using testing::Test;
+
+namespace apmanager {
+
+class ErrorTest : public Test {
+ public:
+  ErrorTest() {}
+
+  void PopulateError(Error* error, Error::Type type) {
+    error->type_ = type;
+  }
+
+  void PopulateError(Error* error, Error::Type type, string message) {
+    error->type_ = type;
+    error->message_ = message;
+  }
+
+  void VerifyDBusError(Error::Type type, const string& expected_error_code) {
+    static const std::string kMessage = "Test error message";
+    Error e;
+    PopulateError(&e, type, kMessage);
+    brillo::ErrorPtr dbus_error;
+    EXPECT_TRUE(e.ToDBusError(&dbus_error));
+    EXPECT_NE(nullptr, dbus_error.get());
+    EXPECT_EQ(brillo::errors::dbus::kDomain, dbus_error->GetDomain());
+    EXPECT_EQ(expected_error_code, dbus_error->GetCode());
+    EXPECT_EQ(kMessage, dbus_error->GetMessage());
+  }
+};
+
+TEST_F(ErrorTest, Constructor) {
+  Error e;
+  EXPECT_EQ(Error::kSuccess, e.type());
+}
+
+TEST_F(ErrorTest, Reset) {
+  Error e;
+  PopulateError(&e, Error::kInternalError);
+  EXPECT_TRUE(e.IsFailure());
+  e.Reset();
+  EXPECT_TRUE(e.IsSuccess());
+}
+
+TEST_F(ErrorTest, ToDBusError) {
+  brillo::ErrorPtr dbus_error;
+
+  // No error.
+  EXPECT_EQ(nullptr, dbus_error.get());
+  EXPECT_FALSE(Error().ToDBusError(&dbus_error));
+  EXPECT_EQ(nullptr, dbus_error.get());
+
+  VerifyDBusError(Error::kInternalError, kErrorInternalError);
+  VerifyDBusError(Error::kInvalidArguments, kErrorInvalidArguments);
+  VerifyDBusError(Error::kInvalidConfiguration, kErrorInvalidConfiguration);
+}
+
+}  // namespace shill