Merge branch 'rewrite-apmanager' into merge-apmanager

BUG:23620021
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..ea7e34d
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+zqiu@chromium.org
diff --git a/apmanager.gyp b/apmanager.gyp
new file mode 100644
index 0000000..4afea9a
--- /dev/null
+++ b/apmanager.gyp
@@ -0,0 +1,136 @@
+{
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'libchrome-<(libbase_ver)',
+        'libchromeos-<(libbase_ver)',
+      ],
+    },
+    'cflags': [
+      '-Wextra',
+      '-Wno-unused-parameter',  # base/lazy_instance.h, etc.
+    ],
+    'cflags_cc': [
+      '-Wno-missing-field-initializers', # for LAZY_INSTANCE_INITIALIZER
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'apmanager-adaptors',
+      'type': 'none',
+      'variables': {
+        'dbus_adaptors_out_dir': 'include/apmanager/dbus_adaptors',
+      },
+      'sources': [
+        'dbus_bindings/org.chromium.apmanager.Config.xml',
+        'dbus_bindings/org.chromium.apmanager.Device.xml',
+        'dbus_bindings/org.chromium.apmanager.Manager.xml',
+        'dbus_bindings/org.chromium.apmanager.Service.xml',
+      ],
+      'includes': ['../common-mk/generate-dbus-adaptors.gypi'],
+    },
+    {
+      'target_name': 'libapmanager',
+      'type': 'static_library',
+      'dependencies': [
+        'apmanager-adaptors',
+      ],
+      'variables': {
+        'exported_deps': [
+          'libshill-net-<(libbase_ver)',
+        ],
+        'deps': ['<@(exported_deps)'],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'sources': [
+        'config.cc',
+        'daemon.cc',
+        'device.cc',
+        'device_info.cc',
+        'dhcp_server.cc',
+        'dhcp_server_factory.cc',
+        'event_dispatcher.cc',
+        'file_writer.cc',
+        'firewall_manager.cc',
+        'hostapd_monitor.cc',
+        'manager.cc',
+        'process_factory.cc',
+        'service.cc',
+        'shill_proxy.cc',
+      ],
+      'actions': [
+        {
+          'action_name': 'generate-shill-proxies',
+          'variables': {
+            'proxy_output_file': 'include/shill/dbus-proxies.h'
+          },
+          'sources': [
+            '../shill/dbus_bindings/org.chromium.flimflam.Manager.xml',
+          ],
+          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+        },
+        {
+          'action_name': 'generate-permission_broker-proxies',
+          'variables': {
+            'proxy_output_file': 'include/permission_broker/dbus-proxies.h'
+          },
+          'sources': [
+            '../permission_broker/dbus_bindings/org.chromium.PermissionBroker.xml',
+          ],
+          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+        },
+      ],
+    },
+    {
+      'target_name': 'apmanager',
+      'type': 'executable',
+      'dependencies': ['libapmanager'],
+      'variables': {
+        'deps': [
+          'libminijail',
+        ],
+      },
+      'sources': [
+        'main.cc',
+      ],
+    },
+  ],
+  'conditions': [
+    ['USE_test == 1', {
+      'targets': [
+        {
+          'target_name': 'apmanager_testrunner',
+          'type': 'executable',
+          'dependencies': ['libapmanager'],
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'config_unittest.cc',
+            'device_info_unittest.cc',
+            'device_unittest.cc',
+            'dhcp_server_unittest.cc',
+            'hostapd_monitor_unittest.cc',
+            'manager_unittest.cc',
+            'mock_config.cc',
+            'mock_device.cc',
+            'mock_dhcp_server.cc',
+            'mock_dhcp_server_factory.cc',
+            'mock_event_dispatcher.cc',
+            'mock_file_writer.cc',
+            'mock_hostapd_monitor.cc',
+            'mock_manager.cc',
+            'mock_process_factory.cc',
+            'mock_service.cc',
+            'service_unittest.cc',
+            'testrunner.cc',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/config.cc b/config.cc
new file mode 100644
index 0000000..8ad2d87
--- /dev/null
+++ b/config.cc
@@ -0,0 +1,420 @@
+// 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 "apmanager/config.h"
+
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "apmanager/daemon.h"
+#include "apmanager/device.h"
+#include "apmanager/manager.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::ExportedObjectManager;
+using chromeos::ErrorPtr;
+using std::string;
+
+namespace apmanager {
+
+// static
+const char Config::kHostapdConfigKeyBridgeInterface[] = "bridge";
+const char Config::kHostapdConfigKeyChannel[] = "channel";
+const char Config::kHostapdConfigKeyControlInterface[] = "ctrl_interface";
+const char Config::kHostapdConfigKeyControlInterfaceGroup[] =
+    "ctrl_interface_group";
+const char Config::kHostapdConfigKeyDriver[] = "driver";
+const char Config::kHostapdConfigKeyFragmThreshold[] = "fragm_threshold";
+const char Config::kHostapdConfigKeyHTCapability[] = "ht_capab";
+const char Config::kHostapdConfigKeyHwMode[] = "hw_mode";
+const char Config::kHostapdConfigKeyIeee80211ac[] = "ieee80211ac";
+const char Config::kHostapdConfigKeyIeee80211n[] = "ieee80211n";
+const char Config::kHostapdConfigKeyIgnoreBroadcastSsid[] =
+    "ignore_broadcast_ssid";
+const char Config::kHostapdConfigKeyInterface[] = "interface";
+const char Config::kHostapdConfigKeyRsnPairwise[] = "rsn_pairwise";
+const char Config::kHostapdConfigKeyRtsThreshold[] = "rts_threshold";
+const char Config::kHostapdConfigKeySsid[] = "ssid";
+const char Config::kHostapdConfigKeyWepDefaultKey[] = "wep_default_key";
+const char Config::kHostapdConfigKeyWepKey0[] = "wep_key0";
+const char Config::kHostapdConfigKeyWpa[] = "wpa";
+const char Config::kHostapdConfigKeyWpaKeyMgmt[] = "wpa_key_mgmt";
+const char Config::kHostapdConfigKeyWpaPassphrase[] = "wpa_passphrase";
+
+const char Config::kHostapdHwMode80211a[] = "a";
+const char Config::kHostapdHwMode80211b[] = "b";
+const char Config::kHostapdHwMode80211g[] = "g";
+
+// static
+const uint16_t Config::kPropertyDefaultChannel = 6;
+const uint16_t Config::kPropertyDefaultServerAddressIndex = 0;
+const bool Config::kPropertyDefaultHiddenNetwork = false;
+
+// static
+const char Config::kHostapdDefaultDriver[] = "nl80211";
+const char Config::kHostapdDefaultRsnPairwise[] = "CCMP";
+const char Config::kHostapdDefaultWpaKeyMgmt[] = "WPA-PSK";
+// Fragmentation threshold: disabled.
+const int Config::kHostapdDefaultFragmThreshold = 2346;
+// RTS threshold: disabled.
+const int Config::kHostapdDefaultRtsThreshold = 2347;
+
+// static
+const uint16_t Config::kBand24GHzChannelLow = 1;
+const uint16_t Config::kBand24GHzChannelHigh = 13;
+const uint32_t Config::kBand24GHzBaseFrequency = 2412;
+const uint16_t Config::kBand5GHzChannelLow = 34;
+const uint16_t Config::kBand5GHzChannelHigh = 165;
+const uint16_t Config::kBand5GHzBaseFrequency = 5170;
+
+// static
+const int Config::kSsidMinLength = 1;
+const int Config::kSsidMaxLength = 32;
+const int Config::kPassphraseMinLength = 8;
+const int Config::kPassphraseMaxLength = 63;
+
+Config::Config(Manager* manager, const string& service_path)
+    : org::chromium::apmanager::ConfigAdaptor(this),
+      manager_(manager),
+      dbus_path_(dbus::ObjectPath(
+          base::StringPrintf("%s/config", service_path.c_str()))) {
+  // Initialize default configuration values.
+  SetSecurityMode(kSecurityModeNone);
+  SetHwMode(kHwMode80211g);
+  SetOperationMode(kOperationModeServer);
+  SetServerAddressIndex(kPropertyDefaultServerAddressIndex);
+  SetChannel(kPropertyDefaultChannel);
+  SetHiddenNetwork(kPropertyDefaultHiddenNetwork);
+  SetFullDeviceControl(true);
+}
+
+Config::~Config() {}
+
+// static.
+bool Config::GetFrequencyFromChannel(uint16_t channel, uint32_t* freq) {
+  bool ret_value = true;
+  if (channel >= kBand24GHzChannelLow && channel <= kBand24GHzChannelHigh) {
+    *freq = kBand24GHzBaseFrequency + (channel - kBand24GHzChannelLow) * 5;
+  } else if (channel >= kBand5GHzChannelLow &&
+             channel <= kBand5GHzChannelHigh) {
+    *freq = kBand5GHzBaseFrequency + (channel - kBand5GHzChannelLow) * 5;
+  } else {
+    ret_value = false;
+  }
+  return ret_value;
+}
+
+bool Config::ValidateSsid(ErrorPtr* error, const string& value) {
+  if (value.length() < kSsidMinLength || value.length() > kSsidMaxLength) {
+    chromeos::Error::AddToPrintf(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+        "SSID must contain between %d and %d characters",
+        kSsidMinLength, kSsidMaxLength);
+    return false;
+  }
+  return true;
+}
+
+bool Config::ValidateSecurityMode(ErrorPtr* error, const string& value) {
+  if (value != kSecurityModeNone && value != kSecurityModeRSN) {
+    chromeos::Error::AddToPrintf(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+        "Invalid/unsupported security mode [%s]", value.c_str());
+    return false;
+  }
+  return true;
+}
+
+bool Config::ValidatePassphrase(ErrorPtr* error, const string& value) {
+  if (value.length() < kPassphraseMinLength ||
+      value.length() > kPassphraseMaxLength) {
+    chromeos::Error::AddToPrintf(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+        "Passphrase must contain between %d and %d characters",
+        kPassphraseMinLength, kPassphraseMaxLength);
+    return false;
+  }
+  return true;
+}
+
+bool Config::ValidateHwMode(ErrorPtr* error, const string& value) {
+  if (value != kHwMode80211a && value != kHwMode80211b &&
+      value != kHwMode80211g && value != kHwMode80211n &&
+      value != kHwMode80211ac) {
+    chromeos::Error::AddToPrintf(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+        "Invalid HW mode [%s]", value.c_str());
+    return false;
+  }
+  return true;
+}
+
+bool Config::ValidateOperationMode(ErrorPtr* error, const string& value) {
+  if (value != kOperationModeServer && value != kOperationModeBridge) {
+    chromeos::Error::AddToPrintf(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+        "Invalid operation mode [%s]", value.c_str());
+    return false;
+  }
+  return true;
+}
+
+bool Config::ValidateChannel(ErrorPtr* error, const uint16_t& value) {
+  if ((value >= kBand24GHzChannelLow && value <= kBand24GHzChannelHigh) ||
+      (value >= kBand5GHzChannelLow && value <= kBand5GHzChannelHigh)) {
+    return true;
+  }
+  chromeos::Error::AddToPrintf(
+      error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+      "Invalid channel [%d]", value);
+  return false;
+}
+
+void Config::RegisterAsync(ExportedObjectManager* object_manager,
+                           const scoped_refptr<dbus::Bus>& bus,
+                           AsyncEventSequencer* sequencer) {
+  CHECK(!dbus_object_) << "Already registered";
+  dbus_object_.reset(
+      new chromeos::dbus_utils::DBusObject(
+          object_manager,
+          bus,
+          dbus_path_));
+  RegisterWithDBusObject(dbus_object_.get());
+  dbus_object_->RegisterAsync(
+      sequencer->GetHandler("Config.RegisterAsync() failed.", true));
+}
+
+bool Config::GenerateConfigFile(ErrorPtr* error, string* config_str) {
+  // SSID.
+  string ssid = GetSsid();
+  if (ssid.empty()) {
+    chromeos::Error::AddTo(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+        "SSID not specified");
+    return false;
+  }
+  base::StringAppendF(
+      config_str, "%s=%s\n", kHostapdConfigKeySsid, ssid.c_str());
+
+  // Bridge interface is required for bridge mode operation.
+  if (GetOperationMode() == kOperationModeBridge) {
+    if (GetBridgeInterface().empty()) {
+      chromeos::Error::AddTo(
+          error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+          "Bridge interface not specified, required for bridge mode");
+      return false;
+    }
+    base::StringAppendF(config_str,
+                        "%s=%s\n",
+                        kHostapdConfigKeyBridgeInterface,
+                        GetBridgeInterface().c_str());
+  }
+
+  // Channel.
+  base::StringAppendF(
+      config_str, "%s=%d\n", kHostapdConfigKeyChannel, GetChannel());
+
+  // Interface.
+  if (!AppendInterface(error, config_str)) {
+    return false;
+  }
+
+  // Hardware mode.
+  if (!AppendHwMode(error, config_str)) {
+    return false;
+  }
+
+  // Security mode configurations.
+  if (!AppendSecurityMode(error, config_str)) {
+    return false;
+  }
+
+  // Control interface.
+  if (!control_interface_.empty()) {
+    base::StringAppendF(config_str,
+                        "%s=%s\n",
+                        kHostapdConfigKeyControlInterface,
+                        control_interface_.c_str());
+    base::StringAppendF(config_str,
+                        "%s=%s\n",
+                        kHostapdConfigKeyControlInterfaceGroup,
+                        Daemon::kAPManagerGroupName);
+  }
+
+  // Hostapd default configurations.
+  if (!AppendHostapdDefaults(error, config_str)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool Config::ClaimDevice() {
+  if (!device_) {
+    LOG(ERROR) << "Failed to claim device: device doesn't exist.";
+    return false;
+  }
+  return device_->ClaimDevice(GetFullDeviceControl());
+}
+
+bool Config::ReleaseDevice() {
+  if (!device_) {
+    LOG(ERROR) << "Failed to release device: device doesn't exist.";
+    return false;
+  }
+  return device_->ReleaseDevice();
+}
+
+bool Config::AppendHwMode(ErrorPtr* error, std::string* config_str) {
+  string hw_mode = GetHwMode();
+  string hostapd_hw_mode;
+  if (hw_mode == kHwMode80211a) {
+    hostapd_hw_mode = kHostapdHwMode80211a;
+  } else if (hw_mode == kHwMode80211b) {
+    hostapd_hw_mode = kHostapdHwMode80211b;
+  } else if (hw_mode == kHwMode80211g) {
+    hostapd_hw_mode = kHostapdHwMode80211g;
+  } else if (hw_mode == kHwMode80211n) {
+    // Use 802.11a for 5GHz channel and 802.11g for 2.4GHz channel
+    if (GetChannel() >= 34) {
+      hostapd_hw_mode = kHostapdHwMode80211a;
+    } else {
+      hostapd_hw_mode = kHostapdHwMode80211g;
+    }
+    base::StringAppendF(config_str, "%s=1\n", kHostapdConfigKeyIeee80211n);
+
+    // Get HT Capability.
+    string ht_cap;
+    if (!device_->GetHTCapability(GetChannel(), &ht_cap)) {
+      chromeos::Error::AddTo(
+          error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+          "Failed to get HT Capability");
+      return false;
+    }
+    base::StringAppendF(config_str, "%s=%s\n",
+                        kHostapdConfigKeyHTCapability,
+                        ht_cap.c_str());
+  } else if (hw_mode == kHwMode80211ac) {
+    if (GetChannel() >= 34) {
+      hostapd_hw_mode = kHostapdHwMode80211a;
+    } else {
+      hostapd_hw_mode = kHostapdHwMode80211g;
+    }
+    base::StringAppendF(config_str, "%s=1\n", kHostapdConfigKeyIeee80211ac);
+
+    // TODO(zqiu): Determine VHT Capabilities based on the interface PHY's
+    // capababilites.
+  } else {
+    chromeos::Error::AddToPrintf(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+        "Invalid hardware mode: %s", hw_mode.c_str());
+    return false;
+  }
+
+  base::StringAppendF(
+      config_str, "%s=%s\n", kHostapdConfigKeyHwMode, hostapd_hw_mode.c_str());
+  return true;
+}
+
+bool Config::AppendHostapdDefaults(ErrorPtr* error,
+                                   std::string* config_str) {
+  // Driver: NL80211.
+  base::StringAppendF(
+      config_str, "%s=%s\n", kHostapdConfigKeyDriver, kHostapdDefaultDriver);
+
+  // Fragmentation threshold: disabled.
+  base::StringAppendF(config_str,
+                      "%s=%d\n",
+                      kHostapdConfigKeyFragmThreshold,
+                      kHostapdDefaultFragmThreshold);
+
+  // RTS threshold: disabled.
+  base::StringAppendF(config_str,
+                      "%s=%d\n",
+                      kHostapdConfigKeyRtsThreshold,
+                      kHostapdDefaultRtsThreshold);
+
+  return true;
+}
+
+bool Config::AppendInterface(ErrorPtr* error,
+                             std::string* config_str) {
+  string interface = GetInterfaceName();
+  if (interface.empty()) {
+    // Ask manager for unused ap capable device.
+    device_ = manager_->GetAvailableDevice();
+    if (!device_) {
+      chromeos::Error::AddTo(
+          error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+          "No device available");
+      return false;
+    }
+  } else {
+    device_ = manager_->GetDeviceFromInterfaceName(interface);
+    if (!device_) {
+      chromeos::Error::AddToPrintf(
+          error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+          "Unable to find device for the specified interface [%s]",
+          interface.c_str());
+      return false;
+    }
+    if (device_->GetInUsed()) {
+      chromeos::Error::AddToPrintf(
+          error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+          "Device [%s] for interface [%s] already in use",
+          device_->GetDeviceName().c_str(),
+          interface.c_str());
+      return false;
+    }
+  }
+
+  // Use the preferred AP interface from the device.
+  selected_interface_ = device_->GetPreferredApInterface();
+  base::StringAppendF(config_str,
+                      "%s=%s\n",
+                      kHostapdConfigKeyInterface,
+                      selected_interface_.c_str());
+  return true;
+}
+
+bool Config::AppendSecurityMode(ErrorPtr* error,
+                                std::string* config_str) {
+  string security_mode = GetSecurityMode();
+  if (security_mode == kSecurityModeNone) {
+    // Nothing need to be done for open network.
+    return true;
+  }
+
+  if (security_mode == kSecurityModeRSN) {
+    string passphrase = GetPassphrase();
+    if (passphrase.empty()) {
+      chromeos::Error::AddToPrintf(
+          error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+          "Passphrase not set for security mode: %s", security_mode.c_str());
+      return false;
+    }
+
+    base::StringAppendF(config_str, "%s=2\n", kHostapdConfigKeyWpa);
+    base::StringAppendF(config_str,
+                        "%s=%s\n",
+                        kHostapdConfigKeyRsnPairwise,
+                        kHostapdDefaultRsnPairwise);
+    base::StringAppendF(config_str,
+                        "%s=%s\n",
+                        kHostapdConfigKeyWpaKeyMgmt,
+                        kHostapdDefaultWpaKeyMgmt);
+    base::StringAppendF(config_str,
+                        "%s=%s\n",
+                        kHostapdConfigKeyWpaPassphrase,
+                        passphrase.c_str());
+    return true;
+  }
+
+  chromeos::Error::AddToPrintf(
+      error, FROM_HERE, chromeos::errors::dbus::kDomain, kConfigError,
+      "Invalid security mode: %s", security_mode.c_str());
+  return false;
+}
+
+}  // namespace apmanager
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..653b4d0
--- /dev/null
+++ b/config.h
@@ -0,0 +1,151 @@
+// 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 APMANAGER_CONFIG_H_
+#define APMANAGER_CONFIG_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+#include <chromeos/errors/error.h>
+
+#include "apmanager/dbus_adaptors/org.chromium.apmanager.Config.h"
+
+namespace apmanager {
+
+class Device;
+class Manager;
+
+class Config
+    : public org::chromium::apmanager::ConfigAdaptor,
+      public org::chromium::apmanager::ConfigInterface {
+ public:
+  Config(Manager* manager, const std::string& service_path);
+  virtual ~Config();
+
+  // Override ConfigAdaptor Validate functions.
+  bool ValidateSsid(chromeos::ErrorPtr* error,
+                    const std::string& value) override;
+  bool ValidateSecurityMode(chromeos::ErrorPtr* error,
+                            const std::string& value) override;
+  bool ValidatePassphrase(chromeos::ErrorPtr* error,
+                          const std::string& value) override;
+  bool ValidateHwMode(chromeos::ErrorPtr* error,
+                      const std::string& value) override;
+  bool ValidateOperationMode(chromeos::ErrorPtr* error,
+                             const std::string& value) override;
+  bool ValidateChannel(chromeos::ErrorPtr* error,
+                       const uint16_t& value) override;
+
+  // Calculate the frequency based on the given |channel|. Return true and set
+  // the output |frequency| if is valid channel, false otherwise.
+  static bool GetFrequencyFromChannel(uint16_t channel, uint32_t* freq);
+
+  // Register Config DBus object.
+  void RegisterAsync(
+      chromeos::dbus_utils::ExportedObjectManager* object_manager,
+      const scoped_refptr<dbus::Bus>& bus,
+      chromeos::dbus_utils::AsyncEventSequencer* sequencer);
+
+  // Generate a config file string for a hostapd instance. Raise appropriate
+  // error when encounter invalid configuration. Return true if success,
+  // false otherwise.
+  virtual bool GenerateConfigFile(chromeos::ErrorPtr* error,
+                                  std::string* config_str);
+
+  // Claim and release the device needed for this configuration.
+  virtual bool ClaimDevice();
+  virtual bool ReleaseDevice();
+
+  const std::string& control_interface() const { return control_interface_; }
+  void set_control_interface(const std::string& control_interface) {
+    control_interface_ = control_interface;
+  }
+
+  const std::string& selected_interface() const { return selected_interface_; }
+
+  const dbus::ObjectPath& dbus_path() const { return dbus_path_; }
+
+ private:
+  // Keys used in hostapd config file.
+  static const char kHostapdConfigKeyBridgeInterface[];
+  static const char kHostapdConfigKeyChannel[];
+  static const char kHostapdConfigKeyControlInterface[];
+  static const char kHostapdConfigKeyControlInterfaceGroup[];
+  static const char kHostapdConfigKeyDriver[];
+  static const char kHostapdConfigKeyFragmThreshold[];
+  static const char kHostapdConfigKeyHTCapability[];
+  static const char kHostapdConfigKeyHwMode[];
+  static const char kHostapdConfigKeyIeee80211ac[];
+  static const char kHostapdConfigKeyIeee80211n[];
+  static const char kHostapdConfigKeyIgnoreBroadcastSsid[];
+  static const char kHostapdConfigKeyInterface[];
+  static const char kHostapdConfigKeyRsnPairwise[];
+  static const char kHostapdConfigKeyRtsThreshold[];
+  static const char kHostapdConfigKeySsid[];
+  static const char kHostapdConfigKeyWepDefaultKey[];
+  static const char kHostapdConfigKeyWepKey0[];
+  static const char kHostapdConfigKeyWpa[];
+  static const char kHostapdConfigKeyWpaKeyMgmt[];
+  static const char kHostapdConfigKeyWpaPassphrase[];
+
+  // Hardware mode value for hostapd config file.
+  static const char kHostapdHwMode80211a[];
+  static const char kHostapdHwMode80211b[];
+  static const char kHostapdHwMode80211g[];
+
+  // Default hostapd configuration values. User will not be able to configure
+  // these.
+  static const char kHostapdDefaultDriver[];
+  static const char kHostapdDefaultRsnPairwise[];
+  static const char kHostapdDefaultWpaKeyMgmt[];
+  static const int kHostapdDefaultFragmThreshold;
+  static const int kHostapdDefaultRtsThreshold;
+
+  // Default config property values.
+  static const uint16_t kPropertyDefaultChannel;;
+  static const bool kPropertyDefaultHiddenNetwork;
+  static const uint16_t kPropertyDefaultServerAddressIndex;
+
+  // Constants use for converting channel to frequency.
+  static const uint16_t kBand24GHzChannelLow;
+  static const uint16_t kBand24GHzChannelHigh;
+  static const uint32_t kBand24GHzBaseFrequency;
+  static const uint16_t kBand5GHzChannelLow;
+  static const uint16_t kBand5GHzChannelHigh;
+  static const uint16_t kBand5GHzBaseFrequency;
+
+  static const int kSsidMinLength;
+  static const int kSsidMaxLength;
+  static const int kPassphraseMinLength;
+  static const int kPassphraseMaxLength;
+
+  // Append default hostapd configurations to the config file.
+  bool AppendHostapdDefaults(chromeos::ErrorPtr* error,
+                             std::string* config_str);
+
+  // Append hardware mode related configurations to the config file.
+  bool AppendHwMode(chromeos::ErrorPtr* error, std::string* config_str);
+
+  // Determine/append interface configuration to the config file.
+  bool AppendInterface(chromeos::ErrorPtr* error, std::string* config_str);
+
+  // Append security related configurations to the config file.
+  bool AppendSecurityMode(chromeos::ErrorPtr* error, std::string* config_str);
+
+  Manager* manager_;
+  dbus::ObjectPath dbus_path_;
+  std::string control_interface_;
+  // Interface selected for hostapd.
+  std::string selected_interface_;
+  std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+  scoped_refptr<Device> device_;
+
+  DISALLOW_COPY_AND_ASSIGN(Config);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_CONFIG_H_
diff --git a/config_unittest.cc b/config_unittest.cc
new file mode 100644
index 0000000..aa11c76
--- /dev/null
+++ b/config_unittest.cc
@@ -0,0 +1,404 @@
+// 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 "apmanager/config.h"
+
+#include <string>
+
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "apmanager/mock_device.h"
+#include "apmanager/mock_manager.h"
+
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+namespace apmanager {
+
+namespace {
+
+const char kServicePath[] = "/manager/services/0";
+const char kSsid[] = "TestSsid";
+const char kInterface[] = "uap0";
+const char kBridgeInterface[] = "br0";
+const char kControlInterfacePath[] = "/var/run/apmanager/hostapd/ctrl_iface";
+const char kPassphrase[] = "Passphrase";
+const char k24GHzHTCapab[] = "[LDPC SMPS-STATIC GF SHORT-GI-20]";
+const char k5GHzHTCapab[] =
+    "[LDPC HT40+ SMPS-STATIC GF SHORT-GI-20 SHORT-GI-40]";
+
+const uint16_t k24GHzChannel = 6;
+const uint16_t k5GHzChannel = 36;
+
+const char kExpected80211gConfigContent[] = "ssid=TestSsid\n"
+                                            "channel=6\n"
+                                            "interface=uap0\n"
+                                            "hw_mode=g\n"
+                                            "driver=nl80211\n"
+                                            "fragm_threshold=2346\n"
+                                            "rts_threshold=2347\n";
+
+const char kExpected80211gBridgeConfigContent[] = "ssid=TestSsid\n"
+                                                  "bridge=br0\n"
+                                                  "channel=6\n"
+                                                  "interface=uap0\n"
+                                                  "hw_mode=g\n"
+                                                  "driver=nl80211\n"
+                                                  "fragm_threshold=2346\n"
+                                                  "rts_threshold=2347\n";
+
+const char kExpected80211gCtrlIfaceConfigContent[] =
+    "ssid=TestSsid\n"
+    "channel=6\n"
+    "interface=uap0\n"
+    "hw_mode=g\n"
+    "ctrl_interface=/var/run/apmanager/hostapd/ctrl_iface\n"
+    "ctrl_interface_group=apmanager\n"
+    "driver=nl80211\n"
+    "fragm_threshold=2346\n"
+    "rts_threshold=2347\n";
+
+const char kExpected80211n5GHzConfigContent[] =
+    "ssid=TestSsid\n"
+    "channel=36\n"
+    "interface=uap0\n"
+    "ieee80211n=1\n"
+    "ht_capab=[LDPC HT40+ SMPS-STATIC GF SHORT-GI-20 SHORT-GI-40]\n"
+    "hw_mode=a\n"
+    "driver=nl80211\n"
+    "fragm_threshold=2346\n"
+    "rts_threshold=2347\n";
+
+const char kExpected80211n24GHzConfigContent[] =
+    "ssid=TestSsid\n"
+    "channel=6\n"
+    "interface=uap0\n"
+    "ieee80211n=1\n"
+    "ht_capab=[LDPC SMPS-STATIC GF SHORT-GI-20]\n"
+    "hw_mode=g\n"
+    "driver=nl80211\n"
+    "fragm_threshold=2346\n"
+    "rts_threshold=2347\n";
+
+const char kExpectedRsnConfigContent[] = "ssid=TestSsid\n"
+                                         "channel=6\n"
+                                         "interface=uap0\n"
+                                         "hw_mode=g\n"
+                                         "wpa=2\n"
+                                         "rsn_pairwise=CCMP\n"
+                                         "wpa_key_mgmt=WPA-PSK\n"
+                                         "wpa_passphrase=Passphrase\n"
+                                         "driver=nl80211\n"
+                                         "fragm_threshold=2346\n"
+                                         "rts_threshold=2347\n";
+
+}  // namespace
+
+class ConfigTest : public testing::Test {
+ public:
+  ConfigTest() : config_(&manager_, kServicePath) {}
+
+  void SetupDevice(const std::string& interface) {
+    // Setup mock device.
+    device_ = new MockDevice();
+    device_->SetPreferredApInterface(interface);
+    EXPECT_CALL(manager_, GetDeviceFromInterfaceName(interface))
+        .WillRepeatedly(Return(device_));
+  }
+
+ protected:
+  Config config_;
+  MockManager manager_;
+  scoped_refptr<MockDevice> device_;
+};
+
+MATCHER_P(IsConfigErrorStartingWith, message, "") {
+  return arg != nullptr &&
+         arg->GetDomain() == chromeos::errors::dbus::kDomain &&
+         arg->GetCode() == kConfigError &&
+         base::StartsWithASCII(arg->GetMessage(), message, false);
+}
+
+TEST_F(ConfigTest, GetFrequencyFromChannel) {
+  uint32_t frequency;
+  // Invalid channel.
+  EXPECT_FALSE(Config::GetFrequencyFromChannel(0, &frequency));
+  EXPECT_FALSE(Config::GetFrequencyFromChannel(166, &frequency));
+  EXPECT_FALSE(Config::GetFrequencyFromChannel(14, &frequency));
+  EXPECT_FALSE(Config::GetFrequencyFromChannel(33, &frequency));
+
+  // Valid channel.
+  const uint32_t kChannel1Frequency = 2412;
+  const uint32_t kChannel13Frequency = 2472;
+  const uint32_t kChannel34Frequency = 5170;
+  const uint32_t kChannel165Frequency = 5825;
+  EXPECT_TRUE(Config::GetFrequencyFromChannel(1, &frequency));
+  EXPECT_EQ(kChannel1Frequency, frequency);
+  EXPECT_TRUE(Config::GetFrequencyFromChannel(13, &frequency));
+  EXPECT_EQ(kChannel13Frequency, frequency);
+  EXPECT_TRUE(Config::GetFrequencyFromChannel(34, &frequency));
+  EXPECT_EQ(kChannel34Frequency, frequency);
+  EXPECT_TRUE(Config::GetFrequencyFromChannel(165, &frequency));
+  EXPECT_EQ(kChannel165Frequency, frequency);
+}
+
+TEST_F(ConfigTest, ValidateSsid) {
+  chromeos::ErrorPtr error;
+  // SSID must contain between 1 and 32 characters.
+  EXPECT_TRUE(config_.ValidateSsid(&error, "s"));
+  EXPECT_TRUE(config_.ValidateSsid(&error, std::string(32, 'c')));
+  EXPECT_FALSE(config_.ValidateSsid(&error, ""));
+  EXPECT_FALSE(config_.ValidateSsid(&error, std::string(33, 'c')));
+}
+
+TEST_F(ConfigTest, ValidateSecurityMode) {
+  chromeos::ErrorPtr error;
+  EXPECT_TRUE(config_.ValidateSecurityMode(&error, kSecurityModeNone));
+  EXPECT_TRUE(config_.ValidateSecurityMode(&error, kSecurityModeRSN));
+  EXPECT_FALSE(config_.ValidateSecurityMode(&error, "InvalidSecurityMode"));
+}
+
+TEST_F(ConfigTest, ValidatePassphrase) {
+  chromeos::ErrorPtr error;
+  // Passpharse must contain between 8 and 63 characters.
+  EXPECT_TRUE(config_.ValidatePassphrase(&error, std::string(8, 'c')));
+  EXPECT_TRUE(config_.ValidatePassphrase(&error, std::string(63, 'c')));
+  EXPECT_FALSE(config_.ValidatePassphrase(&error, std::string(7, 'c')));
+  EXPECT_FALSE(config_.ValidatePassphrase(&error, std::string(64, 'c')));
+}
+
+TEST_F(ConfigTest, ValidateHwMode) {
+  chromeos::ErrorPtr error;
+  EXPECT_TRUE(config_.ValidateHwMode(&error, kHwMode80211a));
+  EXPECT_TRUE(config_.ValidateHwMode(&error, kHwMode80211b));
+  EXPECT_TRUE(config_.ValidateHwMode(&error, kHwMode80211g));
+  EXPECT_TRUE(config_.ValidateHwMode(&error, kHwMode80211n));
+  EXPECT_TRUE(config_.ValidateHwMode(&error, kHwMode80211ac));
+  EXPECT_FALSE(config_.ValidateSecurityMode(&error, "InvalidHwMode"));
+}
+
+TEST_F(ConfigTest, ValidateOperationMode) {
+  chromeos::ErrorPtr error;
+  EXPECT_TRUE(config_.ValidateOperationMode(&error, kOperationModeServer));
+  EXPECT_TRUE(config_.ValidateOperationMode(&error, kOperationModeBridge));
+  EXPECT_FALSE(config_.ValidateOperationMode(&error, "InvalidMode"));
+}
+
+TEST_F(ConfigTest, ValidateChannel) {
+  chromeos::ErrorPtr error;
+  EXPECT_TRUE(config_.ValidateChannel(&error, 1));
+  EXPECT_TRUE(config_.ValidateChannel(&error, 13));
+  EXPECT_TRUE(config_.ValidateChannel(&error, 34));
+  EXPECT_TRUE(config_.ValidateChannel(&error, 165));
+  EXPECT_FALSE(config_.ValidateChannel(&error, 0));
+  EXPECT_FALSE(config_.ValidateChannel(&error, 14));
+  EXPECT_FALSE(config_.ValidateChannel(&error, 33));
+  EXPECT_FALSE(config_.ValidateChannel(&error, 166));
+}
+
+TEST_F(ConfigTest, NoSsid) {
+  config_.SetChannel(k24GHzChannel);
+  config_.SetHwMode(kHwMode80211g);
+  config_.SetInterfaceName(kInterface);
+
+  std::string config_content;
+  chromeos::ErrorPtr error;
+  EXPECT_FALSE(config_.GenerateConfigFile(&error, &config_content));
+  EXPECT_THAT(error, IsConfigErrorStartingWith("SSID not specified"));
+}
+
+TEST_F(ConfigTest, NoInterface) {
+  // Basic 80211.g configuration.
+  config_.SetSsid(kSsid);
+  config_.SetChannel(k24GHzChannel);
+  config_.SetHwMode(kHwMode80211g);
+
+  // No device available, fail to generate config file.
+  chromeos::ErrorPtr error;
+  std::string config_content;
+  EXPECT_CALL(manager_, GetAvailableDevice()).WillOnce(Return(nullptr));
+  EXPECT_FALSE(config_.GenerateConfigFile(&error, &config_content));
+  EXPECT_THAT(error, IsConfigErrorStartingWith("No device available"));
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  // Device available, config file should be generated without any problem.
+  scoped_refptr<MockDevice> device = new MockDevice();
+  device->SetPreferredApInterface(kInterface);
+  chromeos::ErrorPtr error1;
+  EXPECT_CALL(manager_, GetAvailableDevice()).WillOnce(Return(device));
+  EXPECT_TRUE(config_.GenerateConfigFile(&error1, &config_content));
+  EXPECT_NE(std::string::npos, config_content.find(
+                                   kExpected80211gConfigContent))
+      << "Expected to find the following config...\n"
+      << kExpected80211gConfigContent << "..within content...\n"
+      << config_content;
+  EXPECT_EQ(nullptr, error1.get());
+  Mock::VerifyAndClearExpectations(&manager_);
+}
+
+TEST_F(ConfigTest, InvalidInterface) {
+  // Basic 80211.g configuration.
+  config_.SetSsid(kSsid);
+  config_.SetChannel(k24GHzChannel);
+  config_.SetHwMode(kHwMode80211g);
+  config_.SetInterfaceName(kInterface);
+
+  // No device available, fail to generate config file.
+  chromeos::ErrorPtr error;
+  std::string config_content;
+  EXPECT_CALL(manager_, GetDeviceFromInterfaceName(kInterface))
+      .WillOnce(Return(nullptr));
+  EXPECT_FALSE(config_.GenerateConfigFile(&error, &config_content));
+  EXPECT_THAT(error,
+              IsConfigErrorStartingWith(
+                  "Unable to find device for the specified interface"));
+  Mock::VerifyAndClearExpectations(&manager_);
+}
+
+TEST_F(ConfigTest, BridgeMode) {
+  config_.SetSsid(kSsid);
+  config_.SetChannel(k24GHzChannel);
+  config_.SetHwMode(kHwMode80211g);
+  config_.SetInterfaceName(kInterface);
+  config_.SetOperationMode(kOperationModeBridge);
+
+  // Bridge interface required for bridge mode.
+  chromeos::ErrorPtr error;
+  std::string config_content;
+  EXPECT_FALSE(config_.GenerateConfigFile(&error, &config_content));
+  EXPECT_THAT(error,
+              IsConfigErrorStartingWith("Bridge interface not specified"));
+
+  // Set bridge interface, config file should be generated without error.
+  config_.SetBridgeInterface(kBridgeInterface);
+  // Setup mock device.
+  SetupDevice(kInterface);
+  chromeos::ErrorPtr error1;
+  std::string config_content1;
+  EXPECT_TRUE(config_.GenerateConfigFile(&error1, &config_content1));
+  EXPECT_NE(std::string::npos, config_content1.find(
+                                   kExpected80211gBridgeConfigContent))
+      << "Expected to find the following config...\n"
+      << kExpected80211gBridgeConfigContent << "..within content...\n"
+      << config_content1;
+  EXPECT_EQ(nullptr, error1.get());
+}
+
+TEST_F(ConfigTest, 80211gConfig) {
+  config_.SetSsid(kSsid);
+  config_.SetChannel(k24GHzChannel);
+  config_.SetHwMode(kHwMode80211g);
+  config_.SetInterfaceName(kInterface);
+
+  // Setup mock device.
+  SetupDevice(kInterface);
+
+  std::string config_content;
+  chromeos::ErrorPtr error;
+  EXPECT_TRUE(config_.GenerateConfigFile(&error, &config_content));
+  EXPECT_NE(std::string::npos, config_content.find(
+                                   kExpected80211gConfigContent))
+      << "Expected to find the following config...\n"
+      << kExpected80211gConfigContent << "..within content...\n"
+      << config_content;
+  EXPECT_EQ(nullptr, error.get());
+}
+
+TEST_F(ConfigTest, 80211gConfigWithControlInterface) {
+  config_.SetSsid(kSsid);
+  config_.SetChannel(k24GHzChannel);
+  config_.SetHwMode(kHwMode80211g);
+  config_.SetInterfaceName(kInterface);
+  config_.set_control_interface(kControlInterfacePath);
+
+  // Setup mock device.
+  SetupDevice(kInterface);
+
+  std::string config_content;
+  chromeos::ErrorPtr error;
+  EXPECT_TRUE(config_.GenerateConfigFile(&error, &config_content));
+  EXPECT_NE(std::string::npos, config_content.find(
+                                   kExpected80211gCtrlIfaceConfigContent))
+      << "Expected to find the following config...\n"
+      << kExpected80211gCtrlIfaceConfigContent << "..within content...\n"
+      << config_content;
+  EXPECT_EQ(nullptr, error.get());
+}
+
+TEST_F(ConfigTest, 80211nConfig) {
+  config_.SetSsid(kSsid);
+  config_.SetHwMode(kHwMode80211n);
+  config_.SetInterfaceName(kInterface);
+
+  // Setup mock device.
+  SetupDevice(kInterface);
+
+  // 5GHz channel.
+  config_.SetChannel(k5GHzChannel);
+  std::string ghz5_config_content;
+  chromeos::ErrorPtr error;
+  std::string ht_capab_5ghz(k5GHzHTCapab);
+  EXPECT_CALL(*device_.get(), GetHTCapability(k5GHzChannel, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(ht_capab_5ghz), Return(true)));
+  EXPECT_TRUE(config_.GenerateConfigFile(&error, &ghz5_config_content));
+  EXPECT_NE(std::string::npos, ghz5_config_content.find(
+                                   kExpected80211n5GHzConfigContent))
+      << "Expected to find the following config...\n"
+      << kExpected80211n5GHzConfigContent << "..within content...\n"
+      << ghz5_config_content;
+  EXPECT_EQ(nullptr, error.get());
+  Mock::VerifyAndClearExpectations(device_.get());
+
+  // 2.4GHz channel.
+  config_.SetChannel(k24GHzChannel);
+  std::string ghz24_config_content;
+  chromeos::ErrorPtr error1;
+  std::string ht_capab_24ghz(k24GHzHTCapab);
+  EXPECT_CALL(*device_.get(), GetHTCapability(k24GHzChannel, _))
+      .WillOnce(DoAll(SetArgumentPointee<1>(ht_capab_24ghz), Return(true)));
+  EXPECT_TRUE(config_.GenerateConfigFile(&error1, &ghz24_config_content));
+  EXPECT_NE(std::string::npos, ghz24_config_content.find(
+                                   kExpected80211n24GHzConfigContent))
+      << "Expected to find the following config...\n"
+      << kExpected80211n24GHzConfigContent << "..within content...\n"
+      << ghz24_config_content;
+  EXPECT_EQ(nullptr, error.get());
+  Mock::VerifyAndClearExpectations(device_.get());
+}
+
+TEST_F(ConfigTest, RsnConfig) {
+  config_.SetSsid(kSsid);
+  config_.SetChannel(k24GHzChannel);
+  config_.SetHwMode(kHwMode80211g);
+  config_.SetInterfaceName(kInterface);
+  config_.SetSecurityMode(kSecurityModeRSN);
+
+  // Setup mock device.
+  SetupDevice(kInterface);
+
+  // Failed due to no passphrase specified.
+  std::string config_content;
+  chromeos::ErrorPtr error;
+  EXPECT_FALSE(config_.GenerateConfigFile(&error, &config_content));
+  EXPECT_THAT(error, IsConfigErrorStartingWith(
+      base::StringPrintf("Passphrase not set for security mode: %s",
+                         kSecurityModeRSN)));
+
+  chromeos::ErrorPtr error1;
+  config_.SetPassphrase(kPassphrase);
+  EXPECT_TRUE(config_.GenerateConfigFile(&error1, &config_content));
+  EXPECT_NE(std::string::npos, config_content.find(
+                                   kExpectedRsnConfigContent))
+      << "Expected to find the following config...\n"
+      << kExpectedRsnConfigContent << "..within content...\n"
+      << config_content;
+  EXPECT_EQ(nullptr, error1.get());
+}
+
+}  // namespace apmanager
diff --git a/daemon.cc b/daemon.cc
new file mode 100644
index 0000000..15d457c
--- /dev/null
+++ b/daemon.cc
@@ -0,0 +1,55 @@
+// 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 "apmanager/daemon.h"
+
+#include <sysexits.h>
+
+#include <base/logging.h>
+#include <base/message_loop/message_loop_proxy.h>
+#include <base/run_loop.h>
+
+namespace apmanager {
+
+namespace {
+const char kAPManagerServiceName[] = "org.chromium.apmanager";
+const char kAPMRootServicePath[] = "/org/chromium/apmanager";
+}  // namespace
+
+// static
+const char Daemon::kAPManagerGroupName[] = "apmanager";
+const char Daemon::kAPManagerUserName[] = "apmanager";
+
+Daemon::Daemon(const base::Closure& startup_callback)
+    : DBusServiceDaemon(kAPManagerServiceName, kAPMRootServicePath),
+      startup_callback_(startup_callback) {
+}
+
+int Daemon::OnInit() {
+  int return_code = chromeos::DBusServiceDaemon::OnInit();
+  if (return_code != EX_OK) {
+    return return_code;
+  }
+
+  // Signal that we've acquired all resources.
+  startup_callback_.Run();
+
+  // Start manager.
+  manager_->Start();
+
+  return EX_OK;
+}
+
+void Daemon::OnShutdown(int* return_code) {
+  manager_.reset();
+  chromeos::DBusServiceDaemon::OnShutdown(return_code);
+}
+
+void Daemon::RegisterDBusObjectsAsync(
+    chromeos::dbus_utils::AsyncEventSequencer* sequencer) {
+  manager_.reset(new apmanager::Manager());
+  manager_->RegisterAsync(object_manager_.get(), bus_, sequencer);
+}
+
+}  // namespace apmanager
diff --git a/daemon.h b/daemon.h
new file mode 100644
index 0000000..cd6af1c
--- /dev/null
+++ b/daemon.h
@@ -0,0 +1,41 @@
+// 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 APMANAGER_DAEMON_H_
+#define APMANAGER_DAEMON_H_
+
+#include <base/callback_forward.h>
+#include <chromeos/daemons/dbus_daemon.h>
+
+#include "apmanager/manager.h"
+
+namespace apmanager {
+
+class Daemon : public chromeos::DBusServiceDaemon {
+ public:
+  // User and group to run the apmanager process.
+  static const char kAPManagerGroupName[];
+  static const char kAPManagerUserName[];
+
+  explicit Daemon(const base::Closure& startup_callback);
+  ~Daemon() = default;
+
+ protected:
+  int OnInit() override;
+  void OnShutdown(int* return_code) override;
+  void RegisterDBusObjectsAsync(
+      chromeos::dbus_utils::AsyncEventSequencer* sequencer) override;
+
+ private:
+  friend class DaemonTest;
+
+  std::unique_ptr<Manager> manager_;
+  base::Closure startup_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(Daemon);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_DAEMON_H_
diff --git a/dbus_bindings/dbus-service-config.json b/dbus_bindings/dbus-service-config.json
new file mode 100644
index 0000000..8410ed4
--- /dev/null
+++ b/dbus_bindings/dbus-service-config.json
@@ -0,0 +1,6 @@
+{
+  "service_name": "org.chromium.apmanager",
+  "object_manager": {
+    "object_path": "/org/chromium/apmanager"
+  }
+}
\ No newline at end of file
diff --git a/dbus_bindings/org.chromium.apmanager.Config.xml b/dbus_bindings/org.chromium.apmanager.Config.xml
new file mode 100644
index 0000000..815a617
--- /dev/null
+++ b/dbus_bindings/org.chromium.apmanager.Config.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.apmanager.Config">
+    <property name="Ssid" type="s" access="readwrite"/>
+    <property name="InterfaceName" type="s" access="readwrite"/>
+    <property name="SecurityMode" type="s" access="readwrite"/>
+    <property name="Passphrase" type="s" access="write"/>
+    <property name="HwMode" type="s" access="readwrite"/>
+    <property name="OperationMode" type="s" access="readwrite"/>
+    <property name="Channel" type="q" access="readwrite"/>
+    <property name="HiddenNetwork" type="b" access="readwrite"/>
+    <property name="BridgeInterface" type="s" access="readwrite"/>
+    <property name="ServerAddressIndex" type="q" access="readwrite"/>
+    <property name="FullDeviceControl" type="b" access="readwrite"/>
+  </interface>
+</node>
diff --git a/dbus_bindings/org.chromium.apmanager.Device.xml b/dbus_bindings/org.chromium.apmanager.Device.xml
new file mode 100644
index 0000000..a885703
--- /dev/null
+++ b/dbus_bindings/org.chromium.apmanager.Device.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.apmanager.Device">
+    <property name="DeviceName" type="s" access="read"/>
+    <property name="InUsed" type="b" access="read"/>
+    <property name="PreferredApInterface" type="s" access="read"/>
+  </interface>
+</node>
diff --git a/dbus_bindings/org.chromium.apmanager.Manager.xml b/dbus_bindings/org.chromium.apmanager.Manager.xml
new file mode 100644
index 0000000..591204f
--- /dev/null
+++ b/dbus_bindings/org.chromium.apmanager.Manager.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/apmanager/Manager"
+      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.apmanager.Manager">
+    <method name="CreateService">
+      <tp:docstring>
+        Create an Access Point service instance.
+      </tp:docstring>
+      <arg name="service" type="o" direction="out">
+        <tp:docstring>
+          Service for managing an access point.
+        </tp:docstring>
+      </arg>
+      <annotation name="org.chromium.DBus.Method.Kind" value="async"/>
+      <annotation name="org.chromium.DBus.Method.IncludeDBusMessage"
+                  value="true"/>
+    </method>
+    <method name="RemoveService">
+      <tp:docstring>
+        Remove the given access point service.
+      </tp:docstring>
+      <arg name="service" type="o" direction="in">
+        <tp:docstring>
+          Service for managing an access point.
+        </tp:docstring>
+      </arg>
+      <annotation name="org.chromium.DBus.Method.IncludeDBusMessage"
+                  value="true"/>
+    </method>
+  </interface>
+</node>
diff --git a/dbus_bindings/org.chromium.apmanager.Service.xml b/dbus_bindings/org.chromium.apmanager.Service.xml
new file mode 100644
index 0000000..c17a2f9
--- /dev/null
+++ b/dbus_bindings/org.chromium.apmanager.Service.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.apmanager.Service">
+    <method name="Start">
+      <tp:docstring>
+        Start the service.
+      </tp:docstring>
+    </method>
+    <method name="Stop">
+      <tp:docstring>
+        Stop the service.
+      </tp:docstring>
+    </method>
+    <property name="Config" type="o" access="read"/>
+    <property name="State" type="s" access="read"/>
+  </interface>
+</node>
diff --git a/dbus_permissions/org.chromium.apmanager.conf b/dbus_permissions/org.chromium.apmanager.conf
new file mode 100644
index 0000000..85a1967
--- /dev/null
+++ b/dbus_permissions/org.chromium.apmanager.conf
@@ -0,0 +1,16 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="org.chromium.apmanager"/>
+        <allow send_destination="org.chromium.apmanager"/>
+    </policy>
+
+    <policy user="apmanager">
+        <allow own="org.chromium.apmanager"/>
+    </policy>
+
+    <policy group="apmanager">
+        <allow send_destination="org.chromium.apmanager" />
+    </policy>
+</busconfig>
diff --git a/device.cc b/device.cc
new file mode 100644
index 0000000..fe8c9d1
--- /dev/null
+++ b/device.cc
@@ -0,0 +1,374 @@
+// 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 "apmanager/device.h"
+
+#include <base/strings/stringprintf.h>
+#include <chromeos/strings/string_utils.h>
+#include <shill/net/attribute_list.h>
+#include <shill/net/ieee80211.h>
+
+#include "apmanager/config.h"
+#include "apmanager/manager.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::ExportedObjectManager;
+using org::chromium::apmanager::ManagerAdaptor;
+using shill::ByteString;
+using std::string;
+
+namespace apmanager {
+
+Device::Device(Manager* manager, const string& device_name)
+    : org::chromium::apmanager::DeviceAdaptor(this),
+      manager_(manager),
+      supports_ap_mode_(false) {
+  SetDeviceName(device_name);
+  SetInUsed(false);
+}
+
+Device::~Device() {}
+
+void Device::RegisterAsync(ExportedObjectManager* object_manager,
+                           const scoped_refptr<dbus::Bus>& bus,
+                           AsyncEventSequencer* sequencer,
+                           int device_identifier) {
+  CHECK(!dbus_object_) << "Already registered";
+  dbus_path_ = dbus::ObjectPath(
+      base::StringPrintf("%s/devices/%d",
+                         ManagerAdaptor::GetObjectPath().value().c_str(),
+                         device_identifier));
+  dbus_object_.reset(
+      new chromeos::dbus_utils::DBusObject(
+          object_manager,
+          bus,
+          dbus_path_));
+  RegisterWithDBusObject(dbus_object_.get());
+  dbus_object_->RegisterAsync(
+      sequencer->GetHandler("Config.RegisterAsync() failed.", true));
+}
+
+void Device::RegisterInterface(const WiFiInterface& new_interface) {
+  LOG(INFO) << "RegisteringInterface " << new_interface.iface_name
+            << " on device " << GetDeviceName();
+  for (const auto& interface : interface_list_) {
+    // Done if interface already in the list.
+    if (interface.iface_index == new_interface.iface_index) {
+      LOG(INFO) << "Interface " << new_interface.iface_name
+                << " already registered.";
+      return;
+    }
+  }
+  interface_list_.push_back(new_interface);
+  UpdatePreferredAPInterface();
+}
+
+void Device::DeregisterInterface(const WiFiInterface& interface) {
+  LOG(INFO) << "DeregisteringInterface " << interface.iface_name
+            << " on device " << GetDeviceName();
+  for (auto it = interface_list_.begin(); it != interface_list_.end(); ++it) {
+    if (it->iface_index == interface.iface_index) {
+      interface_list_.erase(it);
+      UpdatePreferredAPInterface();
+      return;
+    }
+  }
+}
+
+void Device::ParseWiphyCapability(const shill::Nl80211Message& msg) {
+  // Parse NL80211_ATTR_SUPPORTED_IFTYPES for AP mode interface support.
+  shill::AttributeListConstRefPtr supported_iftypes;
+  if (!msg.const_attributes()->ConstGetNestedAttributeList(
+      NL80211_ATTR_SUPPORTED_IFTYPES, &supported_iftypes)) {
+    LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_SUPPORTED_IFTYPES";
+    return;
+  }
+  supported_iftypes->GetFlagAttributeValue(NL80211_IFTYPE_AP,
+                                           &supports_ap_mode_);
+
+  // Parse WiFi band capabilities.
+  shill::AttributeListConstRefPtr wiphy_bands;
+  if (!msg.const_attributes()->ConstGetNestedAttributeList(
+      NL80211_ATTR_WIPHY_BANDS, &wiphy_bands)) {
+    LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_BANDS";
+    return;
+  }
+
+  shill::AttributeIdIterator band_iter(*wiphy_bands);
+  for (; !band_iter.AtEnd(); band_iter.Advance()) {
+    BandCapability band_cap;
+
+    shill::AttributeListConstRefPtr wiphy_band;
+    if (!wiphy_bands->ConstGetNestedAttributeList(band_iter.GetId(),
+                                                  &wiphy_band)) {
+      LOG(WARNING) << "WiFi band " << band_iter.GetId() << " not found";
+      continue;
+    }
+
+    // ...Each band has a FREQS attribute...
+    shill::AttributeListConstRefPtr frequencies;
+    if (!wiphy_band->ConstGetNestedAttributeList(NL80211_BAND_ATTR_FREQS,
+                                                 &frequencies)) {
+      LOG(ERROR) << "BAND " << band_iter.GetId()
+                 << " had no 'frequencies' attribute";
+      continue;
+    }
+
+    // ...And each FREQS attribute contains an array of information about the
+    // frequency...
+    shill::AttributeIdIterator freq_iter(*frequencies);
+    for (; !freq_iter.AtEnd(); freq_iter.Advance()) {
+      shill::AttributeListConstRefPtr frequency;
+      if (frequencies->ConstGetNestedAttributeList(freq_iter.GetId(),
+                                                   &frequency)) {
+        // ...Including the frequency, itself (the part we want).
+        uint32_t frequency_value = 0;
+        if (frequency->GetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ,
+                                            &frequency_value)) {
+          band_cap.frequencies.push_back(frequency_value);
+        }
+      }
+    }
+
+    wiphy_band->GetU16AttributeValue(NL80211_BAND_ATTR_HT_CAPA,
+                                     &band_cap.ht_capability_mask);
+    wiphy_band->GetU16AttributeValue(NL80211_BAND_ATTR_VHT_CAPA,
+                                     &band_cap.vht_capability_mask);
+    band_capability_.push_back(band_cap);
+  }
+}
+
+bool Device::ClaimDevice(bool full_control) {
+  if (GetInUsed()) {
+    LOG(ERROR) << "Failed to claim device [" << GetDeviceName()
+               << "]: already in used.";
+    return false;
+  }
+
+  if (full_control) {
+    for (const auto& interface : interface_list_) {
+      manager_->ClaimInterface(interface.iface_name);
+      claimed_interfaces_.insert(interface.iface_name);
+    }
+  } else {
+    manager_->ClaimInterface(GetPreferredApInterface());
+    claimed_interfaces_.insert(GetPreferredApInterface());
+  }
+  SetInUsed(true);
+  return true;
+}
+
+bool Device::ReleaseDevice() {
+  if (!GetInUsed()) {
+    LOG(ERROR) << "Failed to release device [" << GetDeviceName()
+               << "]: not currently in-used.";
+    return false;
+  }
+
+  for (const auto& interface : claimed_interfaces_) {
+    manager_->ReleaseInterface(interface);
+  }
+  claimed_interfaces_.clear();
+  SetInUsed(false);
+  return true;
+}
+
+bool Device::InterfaceExists(const string& interface_name) {
+  for (const auto& interface : interface_list_) {
+    if (interface.iface_name == interface_name) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool Device::GetHTCapability(uint16_t channel, string* ht_cap) {
+  // Get the band capability based on the channel.
+  BandCapability band_cap;
+  if (!GetBandCapability(channel, &band_cap)) {
+    LOG(ERROR) << "No band capability found for channel " << channel;
+    return false;
+  }
+
+  std::vector<string> ht_capability;
+  // LDPC coding capability.
+  if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskLdpcCoding) {
+    ht_capability.push_back("LDPC");
+  }
+
+  // Supported channel width set.
+  if (band_cap.ht_capability_mask &
+      shill::IEEE_80211::kHTCapMaskSupWidth2040) {
+    // Determine secondary channel is below or above the primary.
+    bool above = false;
+    if (!GetHTSecondaryChannelLocation(channel, &above)) {
+      LOG(ERROR) << "Unable to determine secondary channel location for "
+                 << "channel " << channel;
+      return false;
+    }
+    if (above) {
+      ht_capability.push_back("HT40+");
+    } else {
+      ht_capability.push_back("HT40-");
+    }
+  }
+
+  // Spatial Multiplexing (SM) Power Save.
+  uint16_t power_save_mask =
+      (band_cap.ht_capability_mask >>
+          shill::IEEE_80211::kHTCapMaskSmPsShift) & 0x3;
+  if (power_save_mask == 0) {
+    ht_capability.push_back("SMPS-STATIC");
+  } else if (power_save_mask == 1) {
+    ht_capability.push_back("SMPS-DYNAMIC");
+  }
+
+  // HT-greenfield.
+  if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskGrnFld) {
+    ht_capability.push_back("GF");
+  }
+
+  // Short GI for 20 MHz.
+  if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskSgi20) {
+    ht_capability.push_back("SHORT-GI-20");
+  }
+
+  // Short GI for 40 MHz.
+  if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskSgi40) {
+    ht_capability.push_back("SHORT-GI-40");
+  }
+
+  // Tx STBC.
+  if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskTxStbc) {
+    ht_capability.push_back("TX-STBC");
+  }
+
+  // Rx STBC.
+  uint16_t rx_stbc =
+      (band_cap.ht_capability_mask >>
+          shill::IEEE_80211::kHTCapMaskRxStbcShift) & 0x3;
+  if (rx_stbc == 1) {
+    ht_capability.push_back("RX-STBC1");
+  } else if (rx_stbc == 2) {
+    ht_capability.push_back("RX-STBC12");
+  } else if (rx_stbc == 3) {
+    ht_capability.push_back("RX-STBC123");
+  }
+
+  // HT-delayed Block Ack.
+  if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskDelayBA) {
+    ht_capability.push_back("DELAYED-BA");
+  }
+
+  // Maximum A-MSDU length.
+  if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskMaxAmsdu) {
+    ht_capability.push_back("MAX-AMSDU-7935");
+  }
+
+  // DSSS/CCK Mode in 40 MHz.
+  if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskDsssCck40) {
+    ht_capability.push_back("DSSS_CCK-40");
+  }
+
+  // 40 MHz intolerant.
+  if (band_cap.ht_capability_mask &
+      shill::IEEE_80211::kHTCapMask40MHzIntolerant) {
+    ht_capability.push_back("40-INTOLERANT");
+  }
+
+  *ht_cap = base::StringPrintf("[%s]",
+      chromeos::string_utils::Join(" ", ht_capability).c_str());
+  return true;
+}
+
+bool Device::GetVHTCapability(uint16_t channel, string* vht_cap) {
+  // TODO(zqiu): to be implemented.
+  return false;
+}
+
+// static
+bool Device::GetHTSecondaryChannelLocation(uint16_t channel, bool* above) {
+  bool ret_val = true;
+
+  // Determine secondary channel location base on the channel. Refer to
+  // ht_cap section in hostapd.conf documentation.
+  switch (channel) {
+    case 7:
+    case 8:
+    case 9:
+    case 10:
+    case 11:
+    case 12:
+    case 13:
+    case 40:
+    case 48:
+    case 56:
+    case 64:
+      *above = false;
+      break;
+
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 36:
+    case 44:
+    case 52:
+    case 60:
+      *above = true;
+      break;
+
+    default:
+      ret_val = false;
+      break;
+  }
+
+  return ret_val;
+}
+
+bool Device::GetBandCapability(uint16_t channel, BandCapability* capability) {
+  uint32_t frequency;
+  if (!Config::GetFrequencyFromChannel(channel, &frequency)) {
+    LOG(ERROR) << "Invalid channel " << channel;
+    return false;
+  }
+
+  for (const auto& band : band_capability_) {
+    if (std::find(band.frequencies.begin(),
+                  band.frequencies.end(),
+                  frequency) != band.frequencies.end()) {
+      *capability = band;
+      return true;
+    }
+  }
+  return false;
+}
+
+void Device::UpdatePreferredAPInterface() {
+  // Return if device doesn't support AP interface mode.
+  if (!supports_ap_mode_) {
+    return;
+  }
+
+  // Use the first registered AP mode interface if there is one, otherwise use
+  // the first registered managed mode interface. If none are available, then
+  // no interface can be used for AP operation on this device.
+  WiFiInterface preferred_interface;
+  for (const auto& interface : interface_list_) {
+    if (interface.iface_type == NL80211_IFTYPE_AP) {
+      preferred_interface = interface;
+      break;
+    } else if (interface.iface_type == NL80211_IFTYPE_STATION &&
+               preferred_interface.iface_name.empty()) {
+      preferred_interface = interface;
+    }
+    // Ignore all other interface types.
+  }
+  // Update preferred AP interface property.
+  SetPreferredApInterface(preferred_interface.iface_name);
+}
+
+}  // namespace apmanager
diff --git a/device.h b/device.h
new file mode 100644
index 0000000..36be348
--- /dev/null
+++ b/device.h
@@ -0,0 +1,131 @@
+// 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 APMANAGER_DEVICE_H_
+#define APMANAGER_DEVICE_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+#include <shill/net/byte_string.h>
+#include <shill/net/nl80211_message.h>
+
+#include "apmanager/dbus_adaptors/org.chromium.apmanager.Device.h"
+
+namespace apmanager {
+
+class Manager;
+
+// Abstraction for WiFi Device (PHY). Each device can have one or more
+// interfaces defined on it.
+class Device : public base::RefCounted<Device>,
+               public org::chromium::apmanager::DeviceAdaptor,
+               public org::chromium::apmanager::DeviceInterface {
+ public:
+  struct WiFiInterface {
+    WiFiInterface() : iface_index(0), iface_type(0) {}
+    WiFiInterface(const std::string& in_iface_name,
+                  const std::string& in_device_name,
+                  uint32_t in_iface_index,
+                  uint32_t in_iface_type)
+        : iface_name(in_iface_name),
+          device_name(in_device_name),
+          iface_index(in_iface_index),
+          iface_type(in_iface_type) {}
+    std::string iface_name;
+    std::string device_name;
+    uint32_t iface_index;
+    uint32_t iface_type;
+    bool Equals(const WiFiInterface& other) const {
+      return this->iface_name == other.iface_name &&
+             this->device_name == other.device_name &&
+             this->iface_index == other.iface_index &&
+             this->iface_type == other.iface_type;
+    }
+  };
+
+  struct BandCapability {
+    std::vector<uint32_t> frequencies;
+    uint16_t ht_capability_mask;
+    uint16_t vht_capability_mask;
+  };
+
+  Device(Manager* manager, const std::string& device_name);
+  virtual ~Device();
+
+  // Register Device DBus object.
+  void RegisterAsync(
+      chromeos::dbus_utils::ExportedObjectManager* object_manager,
+      const scoped_refptr<dbus::Bus>& bus,
+      chromeos::dbus_utils::AsyncEventSequencer* sequencer,
+      int device_identifier);
+
+  // Register/deregister WiFi interface on this device.
+  virtual void RegisterInterface(const WiFiInterface& interface);
+  virtual void DeregisterInterface(const WiFiInterface& interface);
+
+  // Parse device capability from NL80211 message.
+  void ParseWiphyCapability(const shill::Nl80211Message& msg);
+
+  // Claim ownership of this device for AP operation. When |full_control| is
+  // set to true, this will claim all interfaces reside on this device.
+  // When it is set to false, this will only claim the interface used for AP
+  // operation.
+  virtual bool ClaimDevice(bool full_control);
+  // Release any claimed interfaces.
+  virtual bool ReleaseDevice();
+
+  // Return true if interface with |interface_name| resides on this device,
+  // false otherwise.
+  virtual bool InterfaceExists(const std::string& interface_name);
+
+  // Get HT and VHT capability string based on the operating channel.
+  // Return true and set the output capability string if such capability
+  // exist for the band the given |channel| is in, false otherwise.
+  virtual bool GetHTCapability(uint16_t channel, std::string* ht_cap);
+  virtual bool GetVHTCapability(uint16_t channel, std::string* vht_cap);
+
+ private:
+  friend class DeviceTest;
+
+  // Get the HT secondary channel location base on the primary channel.
+  // Return true and set the output |above| flag if channel is valid,
+  // otherwise return false.
+  static bool GetHTSecondaryChannelLocation(uint16_t channel, bool* above);
+
+  // Determine preferred interface to used for AP operation based on the list
+  // of interfaces reside on this device
+  void UpdatePreferredAPInterface();
+
+  // Get the capability for the band the given |channel| is in. Return true
+  // and set the output |capability| pointer if such capability exist for the
+  // band the given |channel| is in, false otherwise.
+  bool GetBandCapability(uint16_t channel, BandCapability* capability);
+
+  Manager* manager_;
+
+  // List of WiFi interfaces live on this device (PHY).
+  std::vector<WiFiInterface> interface_list_;
+
+  dbus::ObjectPath dbus_path_;
+  std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+
+  // Flag indicating if this device supports AP mode interface or not.
+  bool supports_ap_mode_;
+
+  // Wiphy band capabilities.
+  std::vector<BandCapability> band_capability_;
+
+  // List of claimed interfaces.
+  std::set<std::string> claimed_interfaces_;
+
+  DISALLOW_COPY_AND_ASSIGN(Device);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_DEVICE_H_
diff --git a/device_info.cc b/device_info.cc
new file mode 100644
index 0000000..6df9cd3
--- /dev/null
+++ b/device_info.cc
@@ -0,0 +1,322 @@
+// 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 "apmanager/device_info.h"
+
+#include <linux/rtnetlink.h>
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <shill/net/ndisc.h>
+#include <shill/net/netlink_attribute.h>
+#include <shill/net/netlink_manager.h>
+#include <shill/net/nl80211_message.h>
+#include <shill/net/rtnl_handler.h>
+#include <shill/net/rtnl_listener.h>
+#include <shill/net/rtnl_message.h>
+
+#include "apmanager/manager.h"
+
+using base::Bind;
+using shill::ByteString;
+using shill::NetlinkManager;
+using shill::NetlinkMessage;
+using shill::Nl80211Message;
+using shill::RTNLHandler;
+using shill::RTNLMessage;
+using shill::RTNLListener;
+using std::map;
+using std::string;
+
+namespace apmanager {
+
+const char DeviceInfo::kDeviceInfoRoot[] = "/sys/class/net";
+const char DeviceInfo::kInterfaceUevent[] = "uevent";
+const char DeviceInfo::kInterfaceUeventWifiSignature[] = "DEVTYPE=wlan\n";
+
+DeviceInfo::DeviceInfo(Manager* manager)
+    : link_callback_(Bind(&DeviceInfo::LinkMsgHandler, Unretained(this))),
+      device_info_root_(kDeviceInfoRoot),
+      manager_(manager),
+      netlink_manager_(NetlinkManager::GetInstance()),
+      rtnl_handler_(RTNLHandler::GetInstance()) {
+}
+
+DeviceInfo::~DeviceInfo() {}
+
+void DeviceInfo::Start() {
+  // Start netlink manager.
+  netlink_manager_->Init();
+  uint16_t nl80211_family_id = netlink_manager_->GetFamily(
+      Nl80211Message::kMessageTypeString,
+      Bind(&Nl80211Message::CreateMessage));
+  if (nl80211_family_id == NetlinkMessage::kIllegalMessageType) {
+    LOG(FATAL) << "Didn't get a legal message type for 'nl80211' messages.";
+  }
+  Nl80211Message::SetMessageType(nl80211_family_id);
+  netlink_manager_->Start();
+
+  // Start enumerating WiFi devices (PHYs).
+  EnumerateDevices();
+
+  // Start RTNL for monitoring network interfaces.
+  rtnl_handler_->Start(RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
+                       RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE |
+                       RTMGRP_ND_USEROPT);
+  link_listener_.reset(
+      new RTNLListener(RTNLHandler::kRequestLink, link_callback_));
+  // Request link infos.
+  rtnl_handler_->RequestDump(RTNLHandler::kRequestLink);
+}
+
+void DeviceInfo::Stop() {
+  link_listener_.reset();
+}
+
+void DeviceInfo::EnumerateDevices() {
+  shill::GetWiphyMessage get_wiphy;
+  get_wiphy.attributes()->SetFlagAttributeValue(NL80211_ATTR_SPLIT_WIPHY_DUMP,
+                                                true);
+  get_wiphy.AddFlag(NLM_F_DUMP);
+  netlink_manager_->SendNl80211Message(
+      &get_wiphy,
+      Bind(&DeviceInfo::OnWiFiPhyInfoReceived, AsWeakPtr()),
+      Bind(&NetlinkManager::OnAckDoNothing),
+      Bind(&NetlinkManager::OnNetlinkMessageError));
+}
+
+void DeviceInfo::OnWiFiPhyInfoReceived(const shill::Nl80211Message& msg) {
+  // Verify NL80211_CMD_NEW_WIPHY.
+  if (msg.command() != shill::NewWiphyMessage::kCommand) {
+    LOG(ERROR) << "Received unexpected command:"
+               << msg.command();
+    return;
+  }
+
+  string device_name;
+  if (!msg.const_attributes()->GetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
+                                                       &device_name)) {
+    LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_NAME";
+    return;
+  }
+
+  if (GetDevice(device_name)) {
+    LOG(INFO) << "Device " << device_name << " already enumerated.";
+    return;
+  }
+
+  scoped_refptr<Device> device = new Device(manager_, device_name);
+  device->ParseWiphyCapability(msg);
+
+  // Register device
+  RegisterDevice(device);
+}
+
+void DeviceInfo::LinkMsgHandler(const RTNLMessage& msg) {
+  DCHECK(msg.type() == RTNLMessage::kTypeLink);
+
+  // Get interface name.
+  if (!msg.HasAttribute(IFLA_IFNAME)) {
+    LOG(ERROR) << "Link event message does not have IFLA_IFNAME!";
+    return;
+  }
+  ByteString b(msg.GetAttribute(IFLA_IFNAME));
+  string iface_name(reinterpret_cast<const char*>(b.GetConstData()));
+
+  int dev_index = msg.interface_index();
+  if (msg.mode() == RTNLMessage::kModeAdd) {
+    AddLinkMsgHandler(iface_name, dev_index);
+  } else if (msg.mode() == RTNLMessage::kModeDelete) {
+    DelLinkMsgHandler(iface_name, dev_index);
+  } else {
+    NOTREACHED();
+  }
+}
+
+void DeviceInfo::AddLinkMsgHandler(const string& iface_name, int iface_index) {
+  // Ignore non-wifi interfaces.
+  if (!IsWifiInterface(iface_name)) {
+    LOG(INFO) << "Ignore link event for non-wifi interface: " << iface_name;
+    return;
+  }
+
+  // Return if interface already existed. Could receive multiple add link event
+  // for a single interface.
+  if (interface_infos_.find(iface_index) != interface_infos_.end()) {
+    LOG(INFO) << "AddLinkMsgHandler: interface " << iface_name
+              << " is already added";
+    return;
+  }
+
+  // Add interface.
+  Device::WiFiInterface wifi_interface;
+  wifi_interface.iface_name = iface_name;
+  wifi_interface.iface_index = iface_index;
+  interface_infos_[iface_index] = wifi_interface;
+
+  // Get interface info.
+  GetWiFiInterfaceInfo(iface_index);
+}
+
+void DeviceInfo::DelLinkMsgHandler(const string& iface_name, int iface_index) {
+  LOG(INFO) << "DelLinkMsgHandler iface_name: " << iface_name
+            << "iface_index: " << iface_index;
+  map<uint32_t, Device::WiFiInterface>::iterator iter =
+      interface_infos_.find(iface_index);
+  if (iter != interface_infos_.end()) {
+    // Deregister interface from the Device.
+    scoped_refptr<Device> device = GetDevice(iter->second.device_name);
+    if (device) {
+      device->DeregisterInterface(iter->second);
+    }
+    interface_infos_.erase(iter);
+  }
+}
+
+bool DeviceInfo::IsWifiInterface(const string& iface_name) {
+  string contents;
+  if (!GetDeviceInfoContents(iface_name, kInterfaceUevent, &contents)) {
+    LOG(INFO) << "Interface " << iface_name << " has no uevent file";
+    return false;
+  }
+
+  if (contents.find(kInterfaceUeventWifiSignature) == string::npos) {
+    LOG(INFO) << "Interface " << iface_name << " is not a WiFi interface";
+    return false;
+  }
+
+  return true;
+}
+
+bool DeviceInfo::GetDeviceInfoContents(const string& iface_name,
+                                       const string& path_name,
+                                       string* contents_out) {
+  return base::ReadFileToString(
+      device_info_root_.Append(iface_name).Append(path_name),
+      contents_out);
+}
+
+void DeviceInfo::GetWiFiInterfaceInfo(int interface_index) {
+  shill::GetInterfaceMessage msg;
+  if (!msg.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                              interface_index)) {
+    LOG(ERROR) << "Unable to set interface index attribute for "
+                  "GetInterface message.  Interface type cannot be "
+                  "determined!";
+    return;
+  }
+
+  netlink_manager_->SendNl80211Message(
+      &msg,
+      Bind(&DeviceInfo::OnWiFiInterfaceInfoReceived, AsWeakPtr()),
+      Bind(&NetlinkManager::OnAckDoNothing),
+      Bind(&NetlinkManager::OnNetlinkMessageError));
+}
+
+void DeviceInfo::OnWiFiInterfaceInfoReceived(const shill::Nl80211Message& msg) {
+  if (msg.command() != NL80211_CMD_NEW_INTERFACE) {
+    LOG(ERROR) << "Message is not a new interface response";
+    return;
+  }
+
+  uint32_t interface_index;
+  if (!msg.const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                    &interface_index)) {
+    LOG(ERROR) << "Message contains no interface index";
+    return;
+  }
+  uint32_t interface_type;
+  if (!msg.const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFTYPE,
+                                                    &interface_type)) {
+    LOG(ERROR) << "Message contains no interface type";
+    return;
+  }
+
+  map<uint32_t, Device::WiFiInterface>::iterator iter =
+      interface_infos_.find(interface_index);
+  if (iter == interface_infos_.end()) {
+    LOG(ERROR) << "Receive WiFi interface info for non-exist interface: "
+               << interface_index;
+    return;
+  }
+  iter->second.iface_type = interface_type;
+
+  // Request PHY info, to know which Device to register this interface to.
+  GetWiFiInterfacePhyInfo(interface_index);
+}
+
+void DeviceInfo::GetWiFiInterfacePhyInfo(uint32_t iface_index) {
+  shill::GetWiphyMessage get_wiphy;
+  get_wiphy.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                               iface_index);
+  netlink_manager_->SendNl80211Message(
+      &get_wiphy,
+      Bind(&DeviceInfo::OnWiFiInterfacePhyInfoReceived,
+           AsWeakPtr(),
+           iface_index),
+      Bind(&NetlinkManager::OnAckDoNothing),
+      Bind(&NetlinkManager::OnNetlinkMessageError));
+}
+
+void DeviceInfo::OnWiFiInterfacePhyInfoReceived(
+    uint32_t iface_index, const shill::Nl80211Message& msg) {
+  // Verify NL80211_CMD_NEW_WIPHY.
+  if (msg.command() != shill::NewWiphyMessage::kCommand) {
+    LOG(ERROR) << "Received unexpected command:"
+               << msg.command();
+    return;
+  }
+
+  map<uint32_t, Device::WiFiInterface>::iterator iter =
+      interface_infos_.find(iface_index);
+  if (iter == interface_infos_.end()) {
+    // Interface is gone by the time we received its PHY info.
+    LOG(ERROR) << "Interface [" << iface_index
+               << "] is deleted when PHY info is received";
+    return;
+  }
+
+  string device_name;
+  if (!msg.const_attributes()->GetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
+                                                       &device_name)) {
+    LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_NAME";
+    return;
+  }
+
+  scoped_refptr<Device> device = GetDevice(device_name);
+  // Create device if it is not enumerated yet.
+  if (!device) {
+    device = new Device(manager_, device_name);
+    device->ParseWiphyCapability(msg);
+
+    // Register device
+    RegisterDevice(device);
+  }
+  iter->second.device_name = device_name;
+
+  device->RegisterInterface(iter->second);
+}
+
+void DeviceInfo::RegisterDevice(scoped_refptr<Device> device) {
+  if (!device) {
+    return;
+  }
+  devices_[device->GetDeviceName()] = device;
+  // Register device with manager.
+  manager_->RegisterDevice(device);
+}
+
+scoped_refptr<Device> DeviceInfo::GetDevice(const string& device_name) {
+  map<string, scoped_refptr<Device>>::iterator iter =
+      devices_.find(device_name);
+  if (iter == devices_.end()) {
+    return nullptr;
+  }
+  return iter->second;
+}
+
+}  // namespace apmanager
diff --git a/device_info.h b/device_info.h
new file mode 100644
index 0000000..afd6521
--- /dev/null
+++ b/device_info.h
@@ -0,0 +1,106 @@
+// 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 APMANAGER_DEVICE_INFO_H_
+#define APMANAGER_DEVICE_INFO_H_
+
+#include <map>
+#include <string>
+
+#include <base/callback.h>
+#include <base/files/file_path.h>
+#include <base/memory/ref_counted.h>
+#include <base/memory/weak_ptr.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "apmanager/device.h"
+
+namespace shill {
+
+class NetlinkManager;
+class Nl80211Message;
+class RTNLHandler;
+class RTNLMessage;
+class RTNLListener;
+
+}  // namespace shill
+
+namespace apmanager {
+
+class Manager;
+
+// DeviceInfo will enumerate WiFi devices (PHYs) during startup and on-demand
+// (when new interface is detected but the corresponding device is not
+// enumerated). And use RTNL to monitor creation/deletion of WiFi interfaces.
+class DeviceInfo : public base::SupportsWeakPtr<DeviceInfo> {
+ public:
+  explicit DeviceInfo(Manager* manager);
+  virtual ~DeviceInfo();
+
+  // Start and stop device detection monitoring.
+  void Start();
+  void Stop();
+
+ private:
+  friend class DeviceInfoTest;
+
+  static const char kDeviceInfoRoot[];
+  static const char kInterfaceUevent[];
+  static const char kInterfaceUeventWifiSignature[];
+
+  // Use nl80211 to enumerate available WiFi PHYs.
+  void EnumerateDevices();
+  void OnWiFiPhyInfoReceived(const shill::Nl80211Message& msg);
+
+  // Handler for RTNL link event.
+  void LinkMsgHandler(const shill::RTNLMessage& msg);
+  void AddLinkMsgHandler(const std::string& iface_name, int iface_index);
+  void DelLinkMsgHandler(const std::string& iface_name, int iface_index);
+
+  // Return true if the specify |iface_name| is a wifi interface, false
+  // otherwise.
+  bool IsWifiInterface(const std::string& iface_name);
+
+  // Return the contents of the device info file |path_name| for interface
+  // |iface_name| in output parameter |contents_out|. Return true if file
+  // read succeed, fales otherwise.
+  bool GetDeviceInfoContents(const std::string& iface_name,
+                             const std::string& path_name,
+                             std::string* contents_out);
+
+  // Use nl80211 to get WiFi interface information for interface on
+  // |iface_index|.
+  void GetWiFiInterfaceInfo(int iface_index);
+  void OnWiFiInterfaceInfoReceived(const shill::Nl80211Message& msg);
+
+  // Use nl80211 to get PHY info for interface on |iface_index|.
+  void GetWiFiInterfacePhyInfo(uint32_t iface_index);
+  void OnWiFiInterfacePhyInfoReceived(
+      uint32_t iface_index, const shill::Nl80211Message& msg);
+
+  scoped_refptr<Device> GetDevice(const std::string& phy_name);
+  void RegisterDevice(scoped_refptr<Device> device);
+
+  // Maps interface index to interface info
+  std::map<uint32_t, Device::WiFiInterface> interface_infos_;
+  // Maps device name to device object. Each device object represents a PHY.
+  std::map<std::string, scoped_refptr<Device>> devices_;
+
+  // RTNL link event callback and listener.
+  base::Callback<void(const shill::RTNLMessage&)> link_callback_;
+  std::unique_ptr<shill::RTNLListener> link_listener_;
+
+  base::FilePath device_info_root_;
+  Manager *manager_;
+
+  // Cache copy of singleton pointers.
+  shill::NetlinkManager* netlink_manager_;
+  shill::RTNLHandler* rtnl_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceInfo);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_DEVICE_INFO_H_
diff --git a/device_info_unittest.cc b/device_info_unittest.cc
new file mode 100644
index 0000000..afe59ec
--- /dev/null
+++ b/device_info_unittest.cc
@@ -0,0 +1,373 @@
+// 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 "apmanager/device_info.h"
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <shill/net/byte_string.h>
+#include <shill/net/mock_netlink_manager.h>
+#include "shill/net/netlink_message_matchers.h"
+#include "shill/net/nl80211_attribute.h"
+#include "shill/net/nl80211_message.h"
+#include <shill/net/rtnl_message.h>
+
+#include "apmanager/mock_device.h"
+#include "apmanager/mock_manager.h"
+
+using shill::ByteString;
+using shill::Nl80211Message;
+using shill::RTNLMessage;
+using std::map;
+using std::string;
+using std::vector;
+using ::testing::_;
+using ::testing::Mock;
+
+namespace apmanager {
+
+namespace {
+
+const char kTestDeviceName[] = "test-phy";
+const char kTestInterface0Name[] = "test-interface0";
+const char kTestInterface1Name[] = "test-interface1";
+const uint32_t kTestInterface0Index = 1000;
+const uint32_t kTestInterface1Index = 1001;
+
+}  // namespace
+
+class DeviceInfoTest : public testing::Test {
+ public:
+  DeviceInfoTest() : device_info_(&manager_) {}
+  virtual ~DeviceInfoTest() {}
+
+  virtual void SetUp() {
+    // Setup temporary directory for device info files.
+    CHECK(temp_dir_.CreateUniqueTempDir());
+    device_info_root_ = temp_dir_.path().Append("sys/class/net");
+    device_info_.device_info_root_ = device_info_root_;
+
+    // Setup mock pointers;
+    device_info_.netlink_manager_ = &netlink_manager_;
+  }
+
+  bool IsWifiInterface(const string& interface_name) {
+    return device_info_.IsWifiInterface(interface_name);
+  }
+
+  void CreateDeviceInfoFile(const string& interface_name,
+                            const string& file_name,
+                            const string& contents) {
+    base::FilePath info_path =
+        device_info_root_.Append(interface_name).Append(file_name);
+    EXPECT_TRUE(base::CreateDirectory(info_path.DirName()));
+    EXPECT_TRUE(base::WriteFile(info_path, contents.c_str(), contents.size()));
+  }
+
+  void SendLinkMsg(RTNLMessage::Mode mode,
+                   uint32_t interface_index,
+                   const string& interface_name) {
+    RTNLMessage message(RTNLMessage::kTypeLink,
+                        mode,
+                        0,
+                        0,
+                        0,
+                        interface_index,
+                        shill::IPAddress::kFamilyIPv4);
+    message.SetAttribute(static_cast<uint16_t>(IFLA_IFNAME),
+                         ByteString(interface_name, true));
+    device_info_.LinkMsgHandler(message);
+  }
+
+  void VerifyInterfaceList(const vector<Device::WiFiInterface>& interfaces) {
+    // Verify number of elements in the interface infos map and interface index
+    // of the elements in the map.
+    EXPECT_EQ(interfaces.size(), device_info_.interface_infos_.size());
+    for (const auto& interface : interfaces) {
+      map<uint32_t, Device::WiFiInterface>::iterator it =
+          device_info_.interface_infos_.find(interface.iface_index);
+      EXPECT_NE(device_info_.interface_infos_.end(), it);
+      EXPECT_TRUE(interface.Equals(it->second));
+    }
+  }
+
+  void VerifyDeviceList(const vector<scoped_refptr<Device>>& devices) {
+    // Verify number of elements in the device map and the elements in the map.
+    EXPECT_EQ(devices.size(), device_info_.devices_.size());
+    for (const auto& device : devices) {
+      map<string, scoped_refptr<Device>>::iterator it =
+          device_info_.devices_.find(device->GetDeviceName());
+      EXPECT_NE(device_info_.devices_.end(), it);
+      EXPECT_EQ(device, it->second);
+    }
+  }
+  void AddInterface(const Device::WiFiInterface& interface) {
+    device_info_.interface_infos_[interface.iface_index] = interface;
+  }
+
+  void OnWiFiPhyInfoReceived(const Nl80211Message& message) {
+    device_info_.OnWiFiPhyInfoReceived(message);
+  }
+
+  void OnWiFiInterfaceInfoReceived(const Nl80211Message& message) {
+    device_info_.OnWiFiInterfaceInfoReceived(message);
+  }
+
+  void OnWiFiInterfacePhyInfoReceived(uint32_t interface_index,
+                                      const Nl80211Message& message) {
+    device_info_.OnWiFiInterfacePhyInfoReceived(interface_index, message);
+  }
+
+  void RegisterDevice(scoped_refptr<Device> device) {
+    device_info_.RegisterDevice(device);
+  }
+
+ protected:
+  DeviceInfo device_info_;
+  MockManager manager_;
+  shill::MockNetlinkManager netlink_manager_;
+  base::ScopedTempDir temp_dir_;
+  base::FilePath device_info_root_;
+};
+
+MATCHER_P2(IsGetInfoMessage, command, index, "") {
+  if (arg->message_type() != Nl80211Message::GetMessageType()) {
+    return false;
+  }
+  const Nl80211Message *msg = reinterpret_cast<const Nl80211Message *>(arg);
+  if (msg->command() != command) {
+    return false;
+  }
+  uint32_t interface_index;
+  if (!msg->const_attributes()->GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                     &interface_index)) {
+    return false;
+  }
+  // kInterfaceIndex is signed, but the attribute as handed from the kernel
+  // is unsigned.  We're silently casting it away with this assignment.
+  uint32_t test_interface_index = index;
+  return interface_index == test_interface_index;
+}
+
+MATCHER_P(IsInterface, interface, "") {
+  return arg.Equals(interface);
+}
+
+MATCHER_P(IsDevice, device_name, "") {
+  return arg->GetDeviceName() == device_name;
+}
+
+TEST_F(DeviceInfoTest, EnumerateDevices) {
+  shill::NewWiphyMessage message;
+
+  // No device name in the message, failed to create device.
+  EXPECT_CALL(manager_, RegisterDevice(_)).Times(0);
+  OnWiFiPhyInfoReceived(message);
+
+  // Device name in the message, device should be created/register to manager.
+  message.attributes()->CreateNl80211Attribute(
+      NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext());
+  message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
+                                                kTestDeviceName);
+  EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1);
+  OnWiFiPhyInfoReceived(message);
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  // Receive a message for a device already created, should not create/register
+  // device again.
+  EXPECT_CALL(manager_, RegisterDevice(_)).Times(0);
+  OnWiFiPhyInfoReceived(message);
+}
+
+TEST_F(DeviceInfoTest, IsWiFiInterface) {
+  // No device info file exist, not a wifi interface.
+  EXPECT_FALSE(IsWifiInterface(kTestInterface0Name));
+
+  // Device info for an ethernet device, not a wifi interface
+  CreateDeviceInfoFile(kTestInterface0Name, "uevent", "INTERFACE=eth0\n");
+  EXPECT_FALSE(IsWifiInterface(kTestInterface0Name));
+
+  // Device info for a wifi interface.
+  CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n");
+  EXPECT_TRUE(IsWifiInterface(kTestInterface1Name));
+}
+
+TEST_F(DeviceInfoTest, InterfaceDetection) {
+  vector<Device::WiFiInterface> interface_list;
+  // Ignore non-wifi interface.
+  SendLinkMsg(RTNLMessage::kModeAdd,
+              kTestInterface0Index,
+              kTestInterface0Name);
+  VerifyInterfaceList(interface_list);
+
+  // AddLink event for wifi interface.
+  CreateDeviceInfoFile(kTestInterface0Name, "uevent", "DEVTYPE=wlan\n");
+  EXPECT_CALL(netlink_manager_, SendNl80211Message(
+      IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface0Index),
+      _, _, _)).Times(1);
+  SendLinkMsg(RTNLMessage::kModeAdd,
+              kTestInterface0Index,
+              kTestInterface0Name);
+  interface_list.push_back(Device::WiFiInterface(
+      kTestInterface0Name, "", kTestInterface0Index, 0));
+  VerifyInterfaceList(interface_list);
+  Mock::VerifyAndClearExpectations(&netlink_manager_);
+
+  // AddLink event for another wifi interface.
+  CreateDeviceInfoFile(kTestInterface1Name, "uevent", "DEVTYPE=wlan\n");
+  EXPECT_CALL(netlink_manager_, SendNl80211Message(
+      IsGetInfoMessage(NL80211_CMD_GET_INTERFACE, kTestInterface1Index),
+      _, _, _)).Times(1);
+  SendLinkMsg(RTNLMessage::kModeAdd,
+              kTestInterface1Index,
+              kTestInterface1Name);
+  interface_list.push_back(Device::WiFiInterface(
+      kTestInterface1Name, "", kTestInterface1Index, 0));
+  VerifyInterfaceList(interface_list);
+  Mock::VerifyAndClearExpectations(&netlink_manager_);
+
+  // AddLink event for an interface that's already added, no change to interface
+  // list.
+  EXPECT_CALL(netlink_manager_, SendNl80211Message(_, _, _, _)).Times(0);
+  SendLinkMsg(RTNLMessage::kModeAdd,
+              kTestInterface0Index,
+              kTestInterface0Name);
+  VerifyInterfaceList(interface_list);
+  Mock::VerifyAndClearExpectations(&netlink_manager_);
+
+  // Remove the first wifi interface.
+  SendLinkMsg(RTNLMessage::kModeDelete,
+              kTestInterface0Index,
+              kTestInterface0Name);
+  interface_list.clear();
+  interface_list.push_back(Device::WiFiInterface(
+      kTestInterface1Name, "", kTestInterface1Index, 0));
+  VerifyInterfaceList(interface_list);
+
+  // Remove the non-exist interface, no change to the list.
+  SendLinkMsg(RTNLMessage::kModeDelete,
+              kTestInterface0Index,
+              kTestInterface0Name);
+  VerifyInterfaceList(interface_list);
+
+  // Remove the last interface, list should be empty now.
+  SendLinkMsg(RTNLMessage::kModeDelete,
+              kTestInterface1Index,
+              kTestInterface1Name);
+  interface_list.clear();
+  VerifyInterfaceList(interface_list);
+}
+
+TEST_F(DeviceInfoTest, ParseWifiInterfaceInfo) {
+  // Add an interface without interface type info.
+  Device::WiFiInterface interface(
+      kTestInterface0Name, "", kTestInterface0Index, 0);
+  AddInterface(interface);
+  vector<Device::WiFiInterface> interface_list;
+  interface_list.push_back(interface);
+
+  // Message contain no interface index, no change to the interface info.
+  shill::NewInterfaceMessage message;
+  OnWiFiInterfaceInfoReceived(message);
+  VerifyInterfaceList(interface_list);
+
+  // Message contain no interface type, no change to the interface info.
+  message.attributes()->CreateNl80211Attribute(
+      NL80211_ATTR_IFINDEX, shill::NetlinkMessage::MessageContext());
+  message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                             kTestInterface0Index);
+  OnWiFiInterfaceInfoReceived(message);
+
+  // Message contain interface type, interface info should be updated with
+  // the interface type, and a new Nl80211 message should be send to query for
+  // the PHY info.
+  EXPECT_CALL(netlink_manager_, SendNl80211Message(
+      IsGetInfoMessage(NL80211_CMD_GET_WIPHY, kTestInterface0Index),
+      _, _, _)).Times(1);
+  message.attributes()->CreateNl80211Attribute(
+      NL80211_ATTR_IFTYPE, shill::NetlinkMessage::MessageContext());
+  message.attributes()->SetU32AttributeValue(NL80211_ATTR_IFTYPE,
+                                             NL80211_IFTYPE_AP);
+  OnWiFiInterfaceInfoReceived(message);
+  interface_list[0].iface_type = NL80211_IFTYPE_AP;
+  VerifyInterfaceList(interface_list);
+}
+
+TEST_F(DeviceInfoTest, ParsePhyInfoForWifiInterface) {
+  // Register a mock device.
+  scoped_refptr<MockDevice> device = new MockDevice();
+  device->SetDeviceName(kTestDeviceName);
+  EXPECT_CALL(manager_, RegisterDevice(_)).Times(1);
+  RegisterDevice(device);
+
+  // PHY info message.
+  shill::NewWiphyMessage message;
+  message.attributes()->CreateNl80211Attribute(
+      NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext());
+  message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
+                                                kTestDeviceName);
+
+  // Receive PHY info message for an interface that have not been detected yet.
+  EXPECT_CALL(*device.get(), RegisterInterface(_)).Times(0);
+  OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message);
+
+  // Pretend interface is detected through AddLink with interface info already
+  // received (interface type), and still missing PHY info for that interface.
+  Device::WiFiInterface interface(
+      kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP);
+  AddInterface(interface);
+
+  // PHY info is received for a detected interface, should register that
+  // interface to the corresponding Device.
+  interface.device_name = kTestDeviceName;
+  EXPECT_CALL(*device.get(),
+              RegisterInterface(IsInterface(interface))).Times(1);
+  OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message);
+}
+
+TEST_F(DeviceInfoTest, ReceivePhyInfoBeforePhyIsEnumerated) {
+  // New interface is detected.
+  Device::WiFiInterface interface(
+      kTestInterface0Name, "", kTestInterface0Index, NL80211_IFTYPE_AP);
+  AddInterface(interface);
+  vector<Device::WiFiInterface> interface_list;
+  interface_list.push_back(interface);
+
+  // Received PHY info for the interface when the corresponding PHY is not
+  // enumerated yet, new device should be created and register to manager.
+  shill::NewWiphyMessage message;
+  message.attributes()->CreateNl80211Attribute(
+      NL80211_ATTR_WIPHY_NAME, shill::NetlinkMessage::MessageContext());
+  message.attributes()->SetStringAttributeValue(NL80211_ATTR_WIPHY_NAME,
+                                                kTestDeviceName);
+  EXPECT_CALL(manager_, RegisterDevice(IsDevice(kTestDeviceName))).Times(1);
+  OnWiFiInterfacePhyInfoReceived(kTestInterface0Index, message);
+  interface_list[0].device_name = kTestDeviceName;
+  VerifyInterfaceList(interface_list);
+}
+
+TEST_F(DeviceInfoTest, RegisterDevice) {
+  vector<scoped_refptr<Device>> device_list;
+
+  // Register a nullptr.
+  RegisterDevice(nullptr);
+  VerifyDeviceList(device_list);
+
+  // Register a device.
+  device_list.push_back(new Device(&manager_, kTestDeviceName));
+  EXPECT_CALL(manager_, RegisterDevice(device_list[0]));
+  RegisterDevice(device_list[0]);
+  VerifyDeviceList(device_list);
+}
+
+}  // namespace apmanager
diff --git a/device_unittest.cc b/device_unittest.cc
new file mode 100644
index 0000000..516ffb3
--- /dev/null
+++ b/device_unittest.cc
@@ -0,0 +1,343 @@
+// 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 "apmanager/device.h"
+
+#include <string>
+#include <vector>
+
+#include <base/strings/stringprintf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <shill/net/ieee80211.h>
+#include <shill/net/nl80211_attribute.h>
+#include <shill/net/nl80211_message.h>
+
+#include "apmanager/mock_manager.h"
+
+using ::testing::_;
+using ::testing::Mock;
+using std::vector;
+
+namespace apmanager {
+
+namespace {
+
+const char kDeviceName[] = "phy0";
+const Device::WiFiInterface kApModeInterface0 = {
+    "uap0", kDeviceName, 1, NL80211_IFTYPE_AP
+};
+const Device::WiFiInterface kApModeInterface1 = {
+    "uap1", kDeviceName, 2, NL80211_IFTYPE_AP
+};
+const Device::WiFiInterface kManagedModeInterface0 = {
+    "wlan0", kDeviceName, 3, NL80211_IFTYPE_STATION
+};
+const Device::WiFiInterface kManagedModeInterface1 = {
+    "wlan1", kDeviceName, 4, NL80211_IFTYPE_STATION
+};
+const Device::WiFiInterface kMonitorModeInterface = {
+    "monitor0", kDeviceName, 5, NL80211_IFTYPE_MONITOR
+};
+
+}  // namespace
+
+class DeviceTest : public testing::Test {
+ public:
+  DeviceTest() : device_(new Device(&manager_, kDeviceName)) {}
+
+  void VerifyInterfaceList(
+      const vector<Device::WiFiInterface>& interface_list) {
+    EXPECT_EQ(interface_list.size(), device_->interface_list_.size());
+    for (size_t i = 0; i < interface_list.size(); i++) {
+      EXPECT_TRUE(interface_list[i].Equals(device_->interface_list_[i]));
+    }
+  }
+
+  void VerifyPreferredApInterface(const std::string& interface_name) {
+    EXPECT_EQ(interface_name, device_->GetPreferredApInterface());
+  }
+
+  void AddWiphyBandAttribute(shill::AttributeListRefPtr wiphy_bands,
+                             const std::string& band_name,
+                             int band_id,
+                             std::vector<uint32_t> frequency_list,
+                             uint16_t ht_cap_mask) {
+    // Band attribute.
+    shill::AttributeListRefPtr wiphy_band;
+    wiphy_bands->CreateNestedAttribute(band_id, band_name.c_str());
+    wiphy_bands->GetNestedAttributeList(band_id, &wiphy_band);
+    // Frequencies attribute.
+    shill::AttributeListRefPtr frequencies;
+    wiphy_band->CreateNestedAttribute(NL80211_BAND_ATTR_FREQS,
+                                      "NL80211_BAND_ATTR_FREQS");
+    wiphy_band->GetNestedAttributeList(NL80211_BAND_ATTR_FREQS,
+                                       &frequencies);
+    // Frequency attribute.
+    for (size_t i = 0; i < frequency_list.size(); i++) {
+      shill::AttributeListRefPtr frequency;
+      frequencies->CreateNestedAttribute(
+          i, base::StringPrintf("Frequency %d", frequency_list[i]).c_str());
+      frequencies->GetNestedAttributeList(i, &frequency);
+      frequency->CreateU32Attribute(NL80211_FREQUENCY_ATTR_FREQ,
+                                    "NL80211_FREQUENCY_ATTR_FREQ");
+      frequency->SetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ,
+                                      frequency_list[i]);
+      frequencies->SetNestedAttributeHasAValue(i);
+    }
+    wiphy_band->SetNestedAttributeHasAValue(NL80211_BAND_ATTR_FREQS);
+
+    // HT Capability attribute.
+    wiphy_band->CreateU16Attribute(NL80211_BAND_ATTR_HT_CAPA,
+                                   "NL80211_BAND_ATTR_HT_CAPA");
+    wiphy_band->SetU16AttributeValue(NL80211_BAND_ATTR_HT_CAPA, ht_cap_mask);
+
+    wiphy_bands->SetNestedAttributeHasAValue(band_id);
+  }
+
+  void EnableApModeSupport() {
+    device_->supports_ap_mode_ = true;
+  }
+
+  void VerifyApModeSupport(bool supports_ap_mode) {
+    EXPECT_EQ(supports_ap_mode, device_->supports_ap_mode_);
+  }
+
+  void VerifyFrequencyList(int band_id, std::vector<uint32_t> frequency_list) {
+    EXPECT_EQ(frequency_list, device_->band_capability_[band_id].frequencies);
+  }
+
+ protected:
+  MockManager manager_;
+  scoped_refptr<Device> device_;
+};
+
+TEST_F(DeviceTest, RegisterInterface) {
+  vector<Device::WiFiInterface> interface_list;
+  interface_list.push_back(kApModeInterface0);
+  interface_list.push_back(kManagedModeInterface0);
+  interface_list.push_back(kMonitorModeInterface);
+
+  device_->RegisterInterface(kApModeInterface0);
+  device_->RegisterInterface(kManagedModeInterface0);
+  device_->RegisterInterface(kMonitorModeInterface);
+
+  // Verify result interface list.
+  VerifyInterfaceList(interface_list);
+}
+
+TEST_F(DeviceTest, DeregisterInterface) {
+  vector<Device::WiFiInterface> interface_list;
+  interface_list.push_back(kApModeInterface0);
+  interface_list.push_back(kManagedModeInterface0);
+
+  // Register all interfaces, then deregister monitor0 and wlan1 interfaces.
+  device_->RegisterInterface(kApModeInterface0);
+  device_->RegisterInterface(kMonitorModeInterface);
+  device_->RegisterInterface(kManagedModeInterface0);
+  device_->RegisterInterface(kManagedModeInterface1);
+  device_->DeregisterInterface(kMonitorModeInterface);
+  device_->DeregisterInterface(kManagedModeInterface1);
+
+  // Verify result interface list.
+  VerifyInterfaceList(interface_list);
+}
+
+TEST_F(DeviceTest, PreferredAPInterface) {
+  EnableApModeSupport();
+
+  // Register a monitor mode interface, no preferred AP mode interface.
+  device_->RegisterInterface(kMonitorModeInterface);
+  VerifyPreferredApInterface("");
+
+  // Register a managed mode interface, should be set to preferred AP interface.
+  device_->RegisterInterface(kManagedModeInterface0);
+  VerifyPreferredApInterface(kManagedModeInterface0.iface_name);
+
+  // Register a ap mode interface, should be set to preferred AP interface.
+  device_->RegisterInterface(kApModeInterface0);
+  VerifyPreferredApInterface(kApModeInterface0.iface_name);
+
+  // Register another ap mode interface "uap1" and managed mode interface
+  // "wlan1", preferred AP interface should still be set to the first detected
+  // ap mode interface "uap0".
+  device_->RegisterInterface(kApModeInterface1);
+  device_->RegisterInterface(kManagedModeInterface1);
+  VerifyPreferredApInterface(kApModeInterface0.iface_name);
+
+  // Deregister the first ap mode interface, preferred AP interface should be
+  // set to the second ap mode interface.
+  device_->DeregisterInterface(kApModeInterface0);
+  VerifyPreferredApInterface(kApModeInterface1.iface_name);
+
+  // Deregister the second ap mode interface, preferred AP interface should be
+  // set the first managed mode interface.
+  device_->DeregisterInterface(kApModeInterface1);
+  VerifyPreferredApInterface(kManagedModeInterface0.iface_name);
+
+  // Deregister the first managed mode interface, preferred AP interface
+  // should be set to the second managed mode interface.
+  device_->DeregisterInterface(kManagedModeInterface0);
+  VerifyPreferredApInterface(kManagedModeInterface1.iface_name);
+
+  // Deregister the second managed mode interface, preferred AP interface
+  // should be set to empty string.
+  device_->DeregisterInterface(kManagedModeInterface1);
+  VerifyPreferredApInterface("");
+}
+
+TEST_F(DeviceTest, DeviceWithoutAPModeSupport) {
+  // AP mode support is not enabled for the device, so no preferred AP
+  // mode interface.
+  device_->RegisterInterface(kApModeInterface0);
+  VerifyPreferredApInterface("");
+}
+
+TEST_F(DeviceTest, ParseWiphyCapability) {
+  shill::NewWiphyMessage message;
+
+  // Supported interface types attribute.
+  message.attributes()->CreateNestedAttribute(
+      NL80211_ATTR_SUPPORTED_IFTYPES, "NL80211_ATTR_SUPPORTED_IFTYPES");
+  shill::AttributeListRefPtr supported_iftypes;
+  message.attributes()->GetNestedAttributeList(
+      NL80211_ATTR_SUPPORTED_IFTYPES, &supported_iftypes);
+  // Add support for AP mode interface.
+  supported_iftypes->CreateFlagAttribute(
+      NL80211_IFTYPE_AP, "NL80211_IFTYPE_AP");
+  supported_iftypes->SetFlagAttributeValue(NL80211_IFTYPE_AP, true);
+  message.attributes()->SetNestedAttributeHasAValue(
+      NL80211_ATTR_SUPPORTED_IFTYPES);
+
+  // Wiphy bands attribute.
+  message.attributes()->CreateNestedAttribute(
+      NL80211_ATTR_WIPHY_BANDS, "NL80211_ATTR_WIPHY_BANDS");
+  shill::AttributeListRefPtr wiphy_bands;
+  message.attributes()->GetNestedAttributeList(
+      NL80211_ATTR_WIPHY_BANDS, &wiphy_bands);
+
+  // 2.4GHz band capability.
+  const uint32_t kBand24GHzFrequencies[] = {
+      2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467};
+  const uint16_t kBand24GHzHTCapMask = shill::IEEE_80211::kHTCapMaskLdpcCoding |
+                                       shill::IEEE_80211::kHTCapMaskGrnFld |
+                                       shill::IEEE_80211::kHTCapMaskSgi20;
+  std::vector<uint32_t> band_24ghz_freq_list(
+      kBand24GHzFrequencies,
+      kBand24GHzFrequencies + sizeof(kBand24GHzFrequencies) /
+          sizeof(kBand24GHzFrequencies[0]));
+  AddWiphyBandAttribute(
+      wiphy_bands, "2.4GHz band", 0, band_24ghz_freq_list,
+      kBand24GHzHTCapMask);
+
+  // 5GHz band capability.
+  const uint32_t kBand5GHzFrequencies[] = {
+      5180, 5190, 5200, 5210, 5220, 5230, 5240, 5260, 5280, 5300, 5320};
+  const uint16_t kBand5GHzHTCapMask =
+      shill::IEEE_80211::kHTCapMaskLdpcCoding |
+      shill::IEEE_80211::kHTCapMaskSupWidth2040 |
+      shill::IEEE_80211::kHTCapMaskGrnFld |
+      shill::IEEE_80211::kHTCapMaskSgi20 |
+      shill::IEEE_80211::kHTCapMaskSgi40;
+  std::vector<uint32_t> band_5ghz_freq_list(
+      kBand5GHzFrequencies,
+      kBand5GHzFrequencies + sizeof(kBand5GHzFrequencies) /
+          sizeof(kBand5GHzFrequencies[0]));
+  AddWiphyBandAttribute(
+      wiphy_bands, "5GHz band", 1, band_5ghz_freq_list, kBand5GHzHTCapMask);
+
+  message.attributes()->SetNestedAttributeHasAValue(NL80211_ATTR_WIPHY_BANDS);
+
+  device_->ParseWiphyCapability(message);
+
+  // Verify AP mode support.
+  VerifyApModeSupport(true);
+
+  // Verify frequency list for both bands.
+  VerifyFrequencyList(0, band_24ghz_freq_list);
+  VerifyFrequencyList(1, band_5ghz_freq_list);
+
+  // Verify HT Capablity for 2.4GHz band.
+  const char kBand24GHzHTCapability[] = "[LDPC SMPS-STATIC GF SHORT-GI-20]";
+  std::string band_24ghz_cap;
+  EXPECT_TRUE(device_->GetHTCapability(6, &band_24ghz_cap));
+  EXPECT_EQ(kBand24GHzHTCapability, band_24ghz_cap);
+
+  // Verify HT Capablity for 5GHz band.
+  const char kBand5GHzHTCapability[] =
+      "[LDPC HT40+ SMPS-STATIC GF SHORT-GI-20 SHORT-GI-40]";
+  std::string band_5ghz_cap;
+  EXPECT_TRUE(device_->GetHTCapability(36, &band_5ghz_cap));
+  EXPECT_EQ(kBand5GHzHTCapability, band_5ghz_cap);
+}
+
+TEST_F(DeviceTest, ClaimAndReleaseDeviceWithFullControl) {
+  EnableApModeSupport();
+
+  // Register multiple interfaces.
+  device_->RegisterInterface(kApModeInterface1);
+  device_->RegisterInterface(kManagedModeInterface1);
+
+  // Claim the device should claim all interfaces registered on this device..
+  EXPECT_CALL(manager_, ClaimInterface(kApModeInterface1.iface_name)).Times(1);
+  EXPECT_CALL(manager_,
+              ClaimInterface(kManagedModeInterface1.iface_name)).Times(1);
+  EXPECT_TRUE(device_->ClaimDevice(true));
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  // Claim the device when it is already claimed.
+  EXPECT_CALL(manager_, ClaimInterface(_)).Times(0);
+  EXPECT_FALSE(device_->ClaimDevice(true));
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  // Release the device should release all interfaces registered on this device.
+  EXPECT_CALL(manager_,
+              ReleaseInterface(kApModeInterface1.iface_name)).Times(1);
+  EXPECT_CALL(manager_,
+              ReleaseInterface(kManagedModeInterface1.iface_name)).Times(1);
+  EXPECT_TRUE(device_->ReleaseDevice());
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  // Release the device when it is not claimed.
+  EXPECT_CALL(manager_, ReleaseInterface(_)).Times(0);
+  EXPECT_FALSE(device_->ReleaseDevice());
+  Mock::VerifyAndClearExpectations(&manager_);
+}
+
+TEST_F(DeviceTest, ClaimAndReleaseDeviceWithoutFullControl) {
+  EnableApModeSupport();
+
+  // Register multiple interfaces.
+  device_->RegisterInterface(kApModeInterface1);
+  device_->RegisterInterface(kManagedModeInterface1);
+
+  // Claim the device should only claim the preferred AP interface registered
+  // on this device.
+  EXPECT_CALL(manager_, ClaimInterface(kApModeInterface1.iface_name)).Times(1);
+  EXPECT_CALL(manager_,
+              ClaimInterface(kManagedModeInterface1.iface_name)).Times(0);
+  EXPECT_TRUE(device_->ClaimDevice(false));
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  // Claim the device when it is already claimed.
+  EXPECT_CALL(manager_, ClaimInterface(_)).Times(0);
+  EXPECT_FALSE(device_->ClaimDevice(false));
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  // Release the device should release the preferred AP interface registered
+  // on this device.
+  EXPECT_CALL(manager_,
+              ReleaseInterface(kApModeInterface1.iface_name)).Times(1);
+  EXPECT_CALL(manager_,
+              ReleaseInterface(kManagedModeInterface1.iface_name)).Times(0);
+  EXPECT_TRUE(device_->ReleaseDevice());
+  Mock::VerifyAndClearExpectations(&manager_);
+
+  // Release the device when it is not claimed.
+  EXPECT_CALL(manager_, ReleaseInterface(_)).Times(0);
+  EXPECT_FALSE(device_->ReleaseDevice());
+  Mock::VerifyAndClearExpectations(&manager_);
+}
+
+}  // namespace apmanager
diff --git a/dhcp_server.cc b/dhcp_server.cc
new file mode 100644
index 0000000..1b3bcf2
--- /dev/null
+++ b/dhcp_server.cc
@@ -0,0 +1,122 @@
+// 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 "apmanager/dhcp_server.h"
+
+#include <net/if.h>
+#include <signal.h>
+
+#include <base/strings/stringprintf.h>
+
+#include "apmanager/daemon.h"
+
+using std::string;
+
+namespace apmanager {
+
+// static.
+const char DHCPServer::kDnsmasqPath[] = "/usr/sbin/dnsmasq";
+const char DHCPServer::kDnsmasqConfigFilePathFormat[] =
+    "/var/run/apmanager/dnsmasq/dhcpd-%d.conf";
+const char DHCPServer::kDHCPLeasesFilePathFormat[] =
+    "/var/run/apmanager/dnsmasq/dhcpd-%d.leases";
+const char DHCPServer::kServerAddressFormat[] = "192.168.%d.254";
+const char DHCPServer::kAddressRangeLowFormat[] = "192.168.%d.1";
+const char DHCPServer::kAddressRangeHighFormat[] = "192.168.%d.128";
+const int DHCPServer::kServerAddressPrefix = 24;
+const int DHCPServer::kTerminationTimeoutSeconds = 2;
+
+DHCPServer::DHCPServer(uint16_t server_address_index,
+                       const string& interface_name)
+    : server_address_index_(server_address_index),
+      interface_name_(interface_name),
+      server_address_(shill::IPAddress::kFamilyIPv4),
+      rtnl_handler_(shill::RTNLHandler::GetInstance()),
+      file_writer_(FileWriter::GetInstance()),
+      process_factory_(ProcessFactory::GetInstance()) {}
+
+DHCPServer::~DHCPServer() {
+  if (dnsmasq_process_) {
+    // The destructor of the Process will send a SIGKILL signal if it is not
+    // already terminated.
+    dnsmasq_process_->Kill(SIGTERM, kTerminationTimeoutSeconds);
+    dnsmasq_process_.reset();
+    rtnl_handler_->RemoveInterfaceAddress(
+        rtnl_handler_->GetInterfaceIndex(interface_name_), server_address_);
+  }
+}
+
+bool DHCPServer::Start() {
+  if (dnsmasq_process_) {
+    LOG(ERROR) << "DHCP Server already running";
+    return false;
+  }
+
+  // Generate dnsmasq config file.
+  string config_str = GenerateConfigFile();
+  string file_name = base::StringPrintf(kDnsmasqConfigFilePathFormat,
+                                        server_address_index_);
+  if (!file_writer_->Write(file_name, config_str)) {
+    LOG(ERROR) << "Failed to write configuration to a file";
+    return false;
+  }
+
+  // Setup local server address and bring up the interface in case it is down.
+  server_address_.SetAddressFromString(
+      base::StringPrintf(kServerAddressFormat, server_address_index_));
+  server_address_.set_prefix(kServerAddressPrefix);
+  int interface_index = rtnl_handler_->GetInterfaceIndex(interface_name_);
+  rtnl_handler_->AddInterfaceAddress(
+      interface_index,
+      server_address_,
+      server_address_.GetDefaultBroadcast(),
+      shill::IPAddress(shill::IPAddress::kFamilyIPv4));
+  rtnl_handler_->SetInterfaceFlags(interface_index, IFF_UP, IFF_UP);
+
+  // Start a dnsmasq process.
+  dnsmasq_process_.reset(process_factory_->CreateProcess());
+  dnsmasq_process_->AddArg(kDnsmasqPath);
+  dnsmasq_process_->AddArg(base::StringPrintf("--conf-file=%s",
+                                              file_name.c_str()));
+  if (!dnsmasq_process_->Start()) {
+    rtnl_handler_->RemoveInterfaceAddress(interface_index, server_address_);
+    dnsmasq_process_.reset();
+    LOG(ERROR) << "Failed to start dnsmasq process";
+    return false;
+  }
+
+  return true;
+}
+
+string DHCPServer::GenerateConfigFile() {
+  string server_address = base::StringPrintf(kServerAddressFormat,
+                                             server_address_index_);
+  string address_low = base::StringPrintf(kAddressRangeLowFormat,
+                                          server_address_index_);
+  string address_high = base::StringPrintf(kAddressRangeHighFormat,
+                                           server_address_index_);
+  string lease_file_path = base::StringPrintf(kDHCPLeasesFilePathFormat,
+                                              server_address_index_);
+  string config;
+  config += "port=0\n";
+  config += "bind-interfaces\n";
+  config += "log-dhcp\n";
+  // By default, dnsmasq process will spawn off another process to run the
+  // dnsmasq task in the "background" and exit the current process immediately.
+  // This means the daemon would not have any knowledge of the background
+  // dnsmasq process, and it will continue to run even after the AP service is
+  // terminated. Configure dnsmasq to run in "foreground" so no extra process
+  // will be spawned.
+  config += "keep-in-foreground\n";
+  // Explicitly set the user to apmanager. If not set, dnsmasq will default to
+  // run as "nobody".
+  base::StringAppendF(&config, "user=%s\n", Daemon::kAPManagerUserName);
+  base::StringAppendF(
+      &config, "dhcp-range=%s,%s\n", address_low.c_str(), address_high.c_str());
+  base::StringAppendF(&config, "interface=%s\n", interface_name_.c_str());
+  base::StringAppendF(&config, "dhcp-leasefile=%s\n", lease_file_path.c_str());
+  return config;
+}
+
+}  // namespace apmanager
diff --git a/dhcp_server.h b/dhcp_server.h
new file mode 100644
index 0000000..f50567a
--- /dev/null
+++ b/dhcp_server.h
@@ -0,0 +1,55 @@
+// 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 APMANAGER_DHCP_SERVER_H_
+#define APMANAGER_DHCP_SERVER_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <shill/net/ip_address.h>
+#include <shill/net/rtnl_handler.h>
+
+#include "apmanager/file_writer.h"
+#include "apmanager/process_factory.h"
+
+namespace apmanager {
+
+class DHCPServer {
+ public:
+  DHCPServer(uint16_t server_address_index,
+             const std::string& interface_name);
+  virtual ~DHCPServer();
+
+  // Start the DHCP server
+  virtual bool Start();
+
+ private:
+  friend class DHCPServerTest;
+
+  std::string GenerateConfigFile();
+
+  static const char kDnsmasqPath[];
+  static const char kDnsmasqConfigFilePathFormat[];
+  static const char kDHCPLeasesFilePathFormat[];
+  static const char kServerAddressFormat[];
+  static const char kAddressRangeLowFormat[];
+  static const char kAddressRangeHighFormat[];
+  static const int kServerAddressPrefix;
+  static const int kTerminationTimeoutSeconds;
+
+  uint16_t server_address_index_;
+  std::string interface_name_;
+  shill::IPAddress server_address_;
+  std::unique_ptr<chromeos::Process> dnsmasq_process_;
+  shill::RTNLHandler* rtnl_handler_;
+  FileWriter* file_writer_;
+  ProcessFactory* process_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(DHCPServer);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_DHCP_SERVER_H_
diff --git a/dhcp_server_factory.cc b/dhcp_server_factory.cc
new file mode 100644
index 0000000..602087b
--- /dev/null
+++ b/dhcp_server_factory.cc
@@ -0,0 +1,28 @@
+// 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 "apmanager/dhcp_server_factory.h"
+
+namespace apmanager {
+
+namespace {
+
+base::LazyInstance<DHCPServerFactory> g_dhcp_server_factory
+    = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+DHCPServerFactory::DHCPServerFactory() {}
+DHCPServerFactory::~DHCPServerFactory() {}
+
+DHCPServerFactory* DHCPServerFactory::GetInstance() {
+  return g_dhcp_server_factory.Pointer();
+}
+
+DHCPServer* DHCPServerFactory::CreateDHCPServer(
+    uint16_t server_addr_index, const std::string& interface_name) {
+  return new DHCPServer(server_addr_index, interface_name);
+}
+
+}  // namespace apmanager
diff --git a/dhcp_server_factory.h b/dhcp_server_factory.h
new file mode 100644
index 0000000..d95e7ca
--- /dev/null
+++ b/dhcp_server_factory.h
@@ -0,0 +1,37 @@
+// 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 APMANAGER_DHCP_SERVER_FACTORY_H_
+#define APMANAGER_DHCP_SERVER_FACTORY_H_
+
+#include <string>
+
+#include <base/lazy_instance.h>
+
+#include "apmanager/dhcp_server.h"
+
+namespace apmanager {
+
+class DHCPServerFactory {
+ public:
+  virtual ~DHCPServerFactory();
+
+  // This is a singleton. Use DHCPServerFactory::GetInstance()->Foo().
+  static DHCPServerFactory* GetInstance();
+
+  virtual DHCPServer* CreateDHCPServer(uint16_t server_address_index,
+                                       const std::string& interface_name);
+
+ protected:
+  DHCPServerFactory();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<DHCPServerFactory>;
+
+  DISALLOW_COPY_AND_ASSIGN(DHCPServerFactory);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_DHCP_SERVER_FACTORY_H_
diff --git a/dhcp_server_unittest.cc b/dhcp_server_unittest.cc
new file mode 100644
index 0000000..dd90546
--- /dev/null
+++ b/dhcp_server_unittest.cc
@@ -0,0 +1,118 @@
+// 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 "apmanager/dhcp_server.h"
+
+#include <string>
+
+#include <net/if.h>
+
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/process_mock.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <shill/net/mock_rtnl_handler.h>
+
+#include "apmanager/mock_file_writer.h"
+#include "apmanager/mock_process_factory.h"
+
+using chromeos::ProcessMock;
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::Return;
+using std::string;
+
+namespace {
+  const uint16_t kServerAddressIndex = 1;
+  const char kTestInterfaceName[] = "test_interface";
+  const char kBinSleep[] = "/bin/sleep";
+  const char kExpectedDnsmasqConfigFile[] =
+      "port=0\n"
+      "bind-interfaces\n"
+      "log-dhcp\n"
+      "keep-in-foreground\n"
+      "user=apmanager\n"
+      "dhcp-range=192.168.1.1,192.168.1.128\n"
+      "interface=test_interface\n"
+      "dhcp-leasefile=/var/run/apmanager/dnsmasq/dhcpd-1.leases\n";
+  const char kDnsmasqConfigFilePath[] =
+      "/var/run/apmanager/dnsmasq/dhcpd-1.conf";
+}  // namespace
+
+namespace apmanager {
+
+class DHCPServerTest : public testing::Test {
+ public:
+  DHCPServerTest()
+      : dhcp_server_(new DHCPServer(kServerAddressIndex, kTestInterfaceName)),
+        rtnl_handler_(new shill::MockRTNLHandler()),
+        file_writer_(MockFileWriter::GetInstance()),
+        process_factory_(MockProcessFactory::GetInstance()) {}
+  virtual ~DHCPServerTest() {}
+
+  virtual void SetUp() {
+    dhcp_server_->rtnl_handler_ = rtnl_handler_.get();
+    dhcp_server_->file_writer_ = file_writer_;
+    dhcp_server_->process_factory_ = process_factory_;
+  }
+
+  virtual void TearDown() {
+    // Reset DHCP server now while RTNLHandler is still valid.
+    dhcp_server_.reset();
+  }
+
+  void StartDummyProcess() {
+    dhcp_server_->dnsmasq_process_.reset(new chromeos::ProcessImpl);
+    dhcp_server_->dnsmasq_process_->AddArg(kBinSleep);
+    dhcp_server_->dnsmasq_process_->AddArg("12345");
+    CHECK(dhcp_server_->dnsmasq_process_->Start());
+  }
+
+  string GenerateConfigFile() {
+    return dhcp_server_->GenerateConfigFile();
+  }
+
+ protected:
+  std::unique_ptr<DHCPServer> dhcp_server_;
+  std::unique_ptr<shill::MockRTNLHandler> rtnl_handler_;
+  MockFileWriter* file_writer_;
+  MockProcessFactory* process_factory_;
+};
+
+
+TEST_F(DHCPServerTest, GenerateConfigFile) {
+  string config_content = GenerateConfigFile();
+  EXPECT_STREQ(kExpectedDnsmasqConfigFile, config_content.c_str())
+      << "Expected to find the following config...\n"
+      << kExpectedDnsmasqConfigFile << ".....\n"
+      << config_content;
+}
+
+TEST_F(DHCPServerTest, StartWhenServerAlreadyStarted) {
+  StartDummyProcess();
+
+  EXPECT_FALSE(dhcp_server_->Start());
+}
+
+TEST_F(DHCPServerTest, StartSuccess) {
+  ProcessMock* process = new ProcessMock();
+
+  const int kInterfaceIndex = 1;
+  EXPECT_CALL(*file_writer_,
+              Write(kDnsmasqConfigFilePath, kExpectedDnsmasqConfigFile))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*rtnl_handler_.get(), GetInterfaceIndex(kTestInterfaceName))
+      .WillOnce(Return(kInterfaceIndex));
+  EXPECT_CALL(*rtnl_handler_.get(),
+      AddInterfaceAddress(kInterfaceIndex, _, _, _)).Times(1);
+  EXPECT_CALL(*rtnl_handler_.get(),
+      SetInterfaceFlags(kInterfaceIndex, IFF_UP, IFF_UP)).Times(1);
+  EXPECT_CALL(*process_factory_, CreateProcess()).WillOnce(Return(process));
+  EXPECT_CALL(*process, Start()).WillOnce(Return(true));
+  EXPECT_TRUE(dhcp_server_->Start());
+  Mock::VerifyAndClearExpectations(rtnl_handler_.get());
+}
+
+}  // namespace apmanager
diff --git a/event_dispatcher.cc b/event_dispatcher.cc
new file mode 100644
index 0000000..c95e4b4
--- /dev/null
+++ b/event_dispatcher.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 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 "apmanager/event_dispatcher.h"
+
+#include <base/location.h>
+#include <base/message_loop/message_loop_proxy.h>
+#include <base/time/time.h>
+
+namespace apmanager {
+
+namespace {
+
+base::LazyInstance<EventDispatcher> g_event_dispatcher
+    = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+EventDispatcher::EventDispatcher() {}
+EventDispatcher::~EventDispatcher() {}
+
+EventDispatcher* EventDispatcher::GetInstance() {
+  return g_event_dispatcher.Pointer();
+}
+
+bool EventDispatcher::PostTask(const base::Closure& task) {
+  return base::MessageLoopProxy::current()->PostTask(FROM_HERE, task);
+}
+
+bool EventDispatcher::PostDelayedTask(const base::Closure& task,
+                                      int64_t delay_ms) {
+  return base::MessageLoopProxy::current()->PostDelayedTask(
+      FROM_HERE, task, base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+}  // namespace apmanager
diff --git a/event_dispatcher.h b/event_dispatcher.h
new file mode 100644
index 0000000..ac4d121
--- /dev/null
+++ b/event_dispatcher.h
@@ -0,0 +1,38 @@
+// Copyright 2015 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 APMANAGER_EVENT_DISPATCHER_H_
+#define APMANAGER_EVENT_DISPATCHER_H_
+
+#include <base/callback.h>
+#include <base/lazy_instance.h>
+
+namespace apmanager {
+
+// Singleton class for dispatching tasks to current message loop.
+class EventDispatcher {
+ public:
+  virtual ~EventDispatcher();
+
+  // This is a singleton. Use EventDispatcher::GetInstance()->Foo().
+  static EventDispatcher* GetInstance();
+
+  // These are thin wrappers around calls of the same name in
+  // <base/message_loop_proxy.h>
+  virtual bool PostTask(const base::Closure& task);
+  virtual bool PostDelayedTask(const base::Closure& task,
+                               int64_t delay_ms);
+
+ protected:
+  EventDispatcher();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<EventDispatcher>;
+
+  DISALLOW_COPY_AND_ASSIGN(EventDispatcher);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_EVENT_DISPATCHER_H_
diff --git a/file_writer.cc b/file_writer.cc
new file mode 100644
index 0000000..b81848b
--- /dev/null
+++ b/file_writer.cc
@@ -0,0 +1,35 @@
+// Copyright 2015 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 "apmanager/file_writer.h"
+
+#include <base/files/file_util.h>
+
+namespace apmanager {
+
+namespace {
+
+base::LazyInstance<FileWriter> g_file_writer
+    = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+FileWriter::FileWriter() {}
+FileWriter::~FileWriter() {}
+
+FileWriter* FileWriter::GetInstance() {
+  return g_file_writer.Pointer();
+}
+
+bool FileWriter::Write(const std::string& file_name,
+                       const std::string& content) {
+  if (base::WriteFile(base::FilePath(file_name),
+                      content.c_str(),
+                      content.size()) == -1) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace apmanager
diff --git a/file_writer.h b/file_writer.h
new file mode 100644
index 0000000..320d623
--- /dev/null
+++ b/file_writer.h
@@ -0,0 +1,36 @@
+// Copyright 2015 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 APMANAGER_FILE_WRITER_H_
+#define APMANAGER_FILE_WRITER_H_
+
+#include <string>
+
+#include <base/lazy_instance.h>
+
+namespace apmanager {
+
+// Singleton class for handling file writes.
+class FileWriter {
+ public:
+  virtual ~FileWriter();
+
+  // This is a singleton. Use FileWriter::GetInstance()->Foo().
+  static FileWriter* GetInstance();
+
+  virtual bool Write(const std::string& file_name,
+                     const std::string& content);
+
+ protected:
+  FileWriter();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<FileWriter>;
+
+  DISALLOW_COPY_AND_ASSIGN(FileWriter);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_FILE_WRITER_H_
diff --git a/firewall_manager.cc b/firewall_manager.cc
new file mode 100644
index 0000000..c26a95e
--- /dev/null
+++ b/firewall_manager.cc
@@ -0,0 +1,177 @@
+// Copyright 2015 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 "apmanager/firewall_manager.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/service_constants.h>
+#include <chromeos/errors/error.h>
+
+using std::string;
+
+namespace apmanager {
+
+namespace {
+
+const uint16_t kDhcpServerPort = 67;
+const int kInvalidFd = -1;
+
+}  // namespace
+
+FirewallManager::FirewallManager()
+    : lifeline_read_fd_(kInvalidFd),
+      lifeline_write_fd_(kInvalidFd) {}
+
+FirewallManager::~FirewallManager() {
+  if (lifeline_read_fd_ != kInvalidFd) {
+    close(lifeline_read_fd_);
+    close(lifeline_write_fd_);
+  }
+}
+
+void FirewallManager::Init(const scoped_refptr<dbus::Bus>& bus) {
+  CHECK(!permission_broker_proxy_) << "Already started";
+
+  if (!SetupLifelinePipe()) {
+    return;
+  }
+
+  permission_broker_proxy_.reset(
+      new org::chromium::PermissionBrokerProxy(
+          bus,
+          permission_broker::kPermissionBrokerServiceName));
+
+  // This will connect the name owner changed signal in DBus object proxy,
+  // The callback will be invoked as soon as service is avalilable. and will
+  // be cleared after it is invoked. So this will be an one time callback.
+  permission_broker_proxy_->GetObjectProxy()->WaitForServiceToBeAvailable(
+      base::Bind(&FirewallManager::OnServiceAvailable, base::Unretained(this)));
+
+  // This will continuously monitor the name owner of the service. However,
+  // it does not connect the name owner changed signal in DBus object proxy
+  // for some reason. In order to connect the name owner changed signal,
+  // either WaitForServiceToBeAvaiable or ConnectToSignal need to be invoked.
+  // Since we're not interested in any signals from the proxy,
+  // WaitForServiceToBeAvailable is used.
+  permission_broker_proxy_->GetObjectProxy()->SetNameOwnerChangedCallback(
+      base::Bind(&FirewallManager::OnServiceNameChanged,
+                 base::Unretained(this)));
+}
+
+void FirewallManager::RequestDHCPPortAccess(const std::string& interface) {
+  CHECK(permission_broker_proxy_) << "Proxy not initialized yet";
+  if (dhcp_access_interfaces_.find(interface) !=
+      dhcp_access_interfaces_.end()) {
+    LOG(ERROR) << "DHCP access already requested for interface: " << interface;
+    return;
+  }
+  RequestUdpPortAccess(interface, kDhcpServerPort);
+  dhcp_access_interfaces_.insert(interface);
+}
+
+void FirewallManager::ReleaseDHCPPortAccess(const std::string& interface) {
+  CHECK(permission_broker_proxy_) << "Proxy not initialized yet";
+  if (dhcp_access_interfaces_.find(interface) ==
+      dhcp_access_interfaces_.end()) {
+    LOG(ERROR) << "DHCP access has not been requested for interface: "
+               << interface;
+    return;
+  }
+  ReleaseUdpPortAccess(interface, kDhcpServerPort);
+  dhcp_access_interfaces_.erase(interface);
+}
+
+bool FirewallManager::SetupLifelinePipe() {
+  if (lifeline_read_fd_ != kInvalidFd) {
+    LOG(ERROR) << "Lifeline pipe already created";
+    return false;
+  }
+
+  // Setup lifeline pipe.
+  int fds[2];
+  if (pipe(fds) != 0) {
+    PLOG(ERROR) << "Failed to create lifeline pipe";
+    return false;
+  }
+  lifeline_read_fd_ = fds[0];
+  lifeline_write_fd_ = fds[1];
+
+  return true;
+}
+
+void FirewallManager::OnServiceAvailable(bool service_available) {
+  LOG(INFO) << "FirewallManager::OnServiceAvailabe " << service_available;
+  // Nothing to be done if proxy service is not available.
+  if (!service_available) {
+    return;
+  }
+  RequestAllPortsAccess();
+}
+
+void FirewallManager::OnServiceNameChanged(const string& old_owner,
+                                           const string& new_owner) {
+  LOG(INFO) << "FirewallManager::OnServiceNameChanged old " << old_owner
+            << " new " << new_owner;
+  // Nothing to be done if no owner is attached to the proxy service.
+  if (new_owner.empty()) {
+    return;
+  }
+  RequestAllPortsAccess();
+}
+
+void FirewallManager::RequestAllPortsAccess() {
+  // Request access to DHCP port for all specified interfaces.
+  for (const auto& dhcp_interface : dhcp_access_interfaces_) {
+    RequestUdpPortAccess(dhcp_interface, kDhcpServerPort);
+  }
+}
+
+void FirewallManager::RequestUdpPortAccess(const string& interface,
+                                           uint16_t port) {
+  bool allowed = false;
+  // Pass the read end of the pipe to permission_broker, for it to monitor this
+  // process.
+  dbus::FileDescriptor fd(lifeline_read_fd_);
+  fd.CheckValidity();
+  chromeos::ErrorPtr error;
+  if (!permission_broker_proxy_->RequestUdpPortAccess(port,
+                                                      interface,
+                                                      fd,
+                                                      &allowed,
+                                                      &error)) {
+    LOG(ERROR) << "Failed to request UDP port access: "
+               << error->GetCode() << " " << error->GetMessage();
+    return;
+  }
+  if (!allowed) {
+    LOG(ERROR) << "Access request for UDP port " << port
+               << " on interface " << interface << " is denied";
+    return;
+  }
+  LOG(INFO) << "Access granted for UDP port " << port
+            << " on interface " << interface;;
+}
+
+void FirewallManager::ReleaseUdpPortAccess(const string& interface,
+                                           uint16_t port) {
+  chromeos::ErrorPtr error;
+  bool success;
+  if (!permission_broker_proxy_->ReleaseUdpPort(port,
+                                                interface,
+                                                &success,
+                                                &error)) {
+    LOG(ERROR) << "Failed to release UDP port access: "
+               << error->GetCode() << " " << error->GetMessage();
+    return;
+  }
+  if (!success) {
+    LOG(ERROR) << "Release request for UDP port " << port
+               << " on interface " << interface << " is denied";
+    return;
+  }
+  LOG(INFO) << "Access released for UDP port " << port
+            << " on interface " << interface;
+}
+
+}  // namespace apmanager
diff --git a/firewall_manager.h b/firewall_manager.h
new file mode 100644
index 0000000..0f81332
--- /dev/null
+++ b/firewall_manager.h
@@ -0,0 +1,66 @@
+// Copyright 2015 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 APMANAGER_FIREWALL_MANAGER_H_
+#define APMANAGER_FIREWALL_MANAGER_H_
+
+#include <set>
+#include <string>
+
+#include <base/macros.h>
+#include <base/memory/scoped_ptr.h>
+
+#include "permission_broker/dbus-proxies.h"
+
+// Class for managing required firewall rules for apmanager.
+namespace apmanager {
+
+class FirewallManager final {
+ public:
+  FirewallManager();
+  ~FirewallManager();
+
+  void Init(const scoped_refptr<dbus::Bus>& bus);
+
+  // Request/release DHCP port access for the specified interface.
+  void RequestDHCPPortAccess(const std::string& interface);
+  void ReleaseDHCPPortAccess(const std::string& interface);
+
+ private:
+  // Setup lifeline pipe to allow the remote firewall server
+  // (permission_broker) to monitor this process, so it can remove the firewall
+  // rules in case this process crashes.
+  bool SetupLifelinePipe();
+
+  void OnServiceAvailable(bool service_available);
+  void OnServiceNameChanged(const std::string& old_owner,
+                            const std::string& new_owner);
+
+  // This is called when a new instance of permission_broker is detected. Since
+  // the new instance doesn't have any knowledge of previously port access
+  // requests, re-issue those requests to permission_broker to get in sync.
+  void RequestAllPortsAccess();
+
+  // Request/release UDP port access for the specified interface and port.
+  void RequestUdpPortAccess(const std::string& interface, uint16_t port);
+  void ReleaseUdpPortAccess(const std::string& interface, uint16_t port);
+
+  // DBus proxy for permission_broker.
+  std::unique_ptr<org::chromium::PermissionBrokerProxy>
+      permission_broker_proxy_;
+  // File descriptors for the two end of the pipe use for communicating with
+  // remote firewall server (permission_broker), where the remote firewall
+  // server will use the read end of the pipe to detect when this process exits.
+  int lifeline_read_fd_;
+  int lifeline_write_fd_;
+
+  // List of interfaces with DHCP port access.
+  std::set<std::string> dhcp_access_interfaces_;
+
+  DISALLOW_COPY_AND_ASSIGN(FirewallManager);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_FIREWALL_MANAGER_H_
diff --git a/hostapd_monitor.cc b/hostapd_monitor.cc
new file mode 100644
index 0000000..e78febc
--- /dev/null
+++ b/hostapd_monitor.cc
@@ -0,0 +1,212 @@
+// Copyright 2015 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 "apmanager/hostapd_monitor.h"
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <shill/net/io_handler_factory_container.h>
+#include <shill/net/sockets.h>
+
+using base::Bind;
+using base::Unretained;
+using shill::IOHandlerFactoryContainer;
+using std::string;
+
+namespace apmanager {
+
+// static.
+const char HostapdMonitor::kLocalPathFormat[] =
+    "/var/run/apmanager/hostapd/hostapd_ctrl_%s";
+const char HostapdMonitor::kHostapdCmdAttach[] = "ATTACH";
+const char HostapdMonitor::kHostapdRespOk[] = "OK\n";
+const char HostapdMonitor::kHostapdEventStationConnected[] = "AP-STA-CONNECTED";
+const char HostapdMonitor::kHostapdEventStationDisconnected[] =
+    "AP-STA-DISCONNECTED";
+const int HostapdMonitor::kHostapdCtrlIfaceCheckIntervalMs = 500;
+const int HostapdMonitor::kHostapdCtrlIfaceCheckMaxAttempts = 5;
+const int HostapdMonitor::kHostapdAttachTimeoutMs = 1000;
+const int HostapdMonitor::kInvalidSocket = -1;
+
+HostapdMonitor::HostapdMonitor(const EventCallback& callback,
+                               const string& control_interface_path,
+                               const string& network_interface_name)
+    : sockets_(new shill::Sockets()),
+      event_callback_(callback),
+      dest_path_(base::StringPrintf("%s/%s",
+                                    control_interface_path.c_str(),
+                                    network_interface_name.c_str())),
+      local_path_(base::StringPrintf(kLocalPathFormat,
+                                     network_interface_name.c_str())),
+      hostapd_socket_(kInvalidSocket),
+      io_handler_factory_(
+          IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()),
+      event_dispatcher_(EventDispatcher::GetInstance()),
+      weak_ptr_factory_(this),
+      started_(false) {}
+
+HostapdMonitor::~HostapdMonitor() {
+  if (hostapd_socket_ != kInvalidSocket) {
+    unlink(local_path_.c_str());
+    sockets_->Close(hostapd_socket_);
+  }
+}
+
+void HostapdMonitor::Start() {
+  if (started_) {
+    LOG(ERROR) << "HostapdMonitor already started";
+    return;
+  }
+
+  hostapd_ctrl_iface_check_count_ = 0;
+  // Start off by checking the control interface file for the hostapd process.
+  event_dispatcher_->PostTask(
+      Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
+           weak_ptr_factory_.GetWeakPtr()));
+  started_ = true;
+}
+
+void HostapdMonitor::HostapdCtrlIfaceCheckTask() {
+  struct stat buf;
+  if (stat(dest_path_.c_str(), &buf) != 0) {
+    if (hostapd_ctrl_iface_check_count_ >= kHostapdCtrlIfaceCheckMaxAttempts) {
+      // This indicates the hostapd failed to start. Invoke callback indicating
+      // hostapd start failed.
+      LOG(ERROR) << "Timeout waiting for hostapd control interface";
+      event_callback_.Run(kHostapdFailed, "");
+    } else {
+      hostapd_ctrl_iface_check_count_++;
+      event_dispatcher_->PostDelayedTask(
+          base::Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
+                     weak_ptr_factory_.GetWeakPtr()),
+          kHostapdCtrlIfaceCheckIntervalMs);
+    }
+    return;
+  }
+
+  // Control interface is up, meaning hostapd started successfully.
+  event_callback_.Run(kHostapdStarted, "");
+
+  // Attach to the control interface to receive unsolicited event notifications.
+  AttachToHostapd();
+}
+
+void HostapdMonitor::AttachToHostapd() {
+  if (hostapd_socket_ != kInvalidSocket) {
+    LOG(ERROR) << "Socket already initialized";
+    return;
+  }
+
+  // Setup socket address for local file and remote file.
+  struct sockaddr_un local;
+  local.sun_family = AF_UNIX;
+  snprintf(local.sun_path, sizeof(local.sun_path), "%s", local_path_.c_str());
+  struct sockaddr_un dest;
+  dest.sun_family = AF_UNIX;
+  snprintf(dest.sun_path, sizeof(dest.sun_path), "%s", dest_path_.c_str());
+
+  // Setup socket for interprocess communication.
+  hostapd_socket_ = sockets_->Socket(PF_UNIX, SOCK_DGRAM, 0);
+  if (hostapd_socket_ < 0) {
+    LOG(ERROR) << "Failed to open hostapd socket";
+    return;
+  }
+  if (sockets_->Bind(hostapd_socket_,
+                     reinterpret_cast<struct sockaddr*>(&local),
+                     sizeof(local)) < 0) {
+    PLOG(ERROR) << "Failed to bind to local socket";
+    return;
+  }
+  if (sockets_->Connect(hostapd_socket_,
+                        reinterpret_cast<struct sockaddr*>(&dest),
+                        sizeof(dest)) < 0) {
+    PLOG(ERROR) << "Failed to connect";
+    return;
+  }
+
+  // Setup IO Input handler.
+  hostapd_input_handler_.reset(io_handler_factory_->CreateIOInputHandler(
+      hostapd_socket_,
+      Bind(&HostapdMonitor::ParseMessage, Unretained(this)),
+      Bind(&HostapdMonitor::OnReadError, Unretained(this))));
+
+  if (!SendMessage(kHostapdCmdAttach, strlen(kHostapdCmdAttach))) {
+    LOG(ERROR) << "Failed to attach to hostapd";
+    return;
+  }
+
+  // Start a timer for ATTACH response.
+  attach_timeout_callback_.Reset(
+      Bind(&HostapdMonitor::AttachTimeoutHandler,
+           weak_ptr_factory_.GetWeakPtr()));
+  event_dispatcher_->PostDelayedTask(attach_timeout_callback_.callback(),
+                                     kHostapdAttachTimeoutMs);
+  return;
+}
+
+void HostapdMonitor::AttachTimeoutHandler() {
+  LOG(ERROR) << "Timeout waiting for attach response";
+}
+
+// Method for sending message to hostapd control interface.
+bool HostapdMonitor::SendMessage(const char* message, size_t length) {
+  if (sockets_->Send(hostapd_socket_, message, length, 0) < 0) {
+    PLOG(ERROR) << "Send to hostapd failed";
+    return false;
+  }
+
+  return true;
+}
+
+void HostapdMonitor::ParseMessage(shill::InputData* data) {
+  string str(reinterpret_cast<const char*>(data->buf), data->len);
+  // "OK" response for the "ATTACH" command.
+  if (str == kHostapdRespOk) {
+    attach_timeout_callback_.Cancel();
+    return;
+  }
+
+  // Event messages are in format of <[Level]>[Event] [Detail message].
+  // For example: <2>AP-STA-CONNECTED 00:11:22:33:44:55
+  // Refer to wpa_ctrl.h for complete list of possible events.
+  if (str.find_first_of('<', 0) == 0 && str.find_first_of('>', 0) == 2) {
+    // Remove the log level.
+    string msg = str.substr(3);
+    string event;
+    string data;
+    size_t pos = msg.find_first_of(' ', 0);
+    if (pos == string::npos) {
+      event = msg;
+    } else {
+      event = msg.substr(0, pos);
+      data = msg.substr(pos + 1);
+    }
+
+    Event event_code;
+    if (event == kHostapdEventStationConnected) {
+      event_code = kStationConnected;
+    } else if (event == kHostapdEventStationDisconnected) {
+      event_code = kStationDisconnected;
+    } else {
+      LOG(INFO) << "Received unknown event: " << event;
+      return;
+    }
+    event_callback_.Run(event_code, data);
+    return;
+  }
+
+  LOG(INFO) << "Received unknown message: " << str;
+}
+
+void HostapdMonitor::OnReadError(const string& error_msg) {
+  LOG(FATAL) << "Hostapd Socket read returns error: "
+             << error_msg;
+}
+
+}  // namespace apmanager
diff --git a/hostapd_monitor.h b/hostapd_monitor.h
new file mode 100644
index 0000000..05ab08b
--- /dev/null
+++ b/hostapd_monitor.h
@@ -0,0 +1,98 @@
+// Copyright 2015 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 APMANAGER_HOSTAPD_MONITOR_H_
+#define APMANAGER_HOSTAPD_MONITOR_H_
+
+#include <string>
+
+#include <base/cancelable_callback.h>
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+
+#include "apmanager/event_dispatcher.h"
+
+namespace shill {
+
+struct InputData;
+class IOHandler;
+class IOHandlerFactory;
+class Sockets;
+
+}  // namespace shill
+
+namespace apmanager {
+
+// Class for monitoring events from hostapd control interface.
+class HostapdMonitor {
+ public:
+  enum Event {
+    kHostapdFailed,
+    kHostapdStarted,
+    kStationConnected,
+    kStationDisconnected,
+  };
+
+  typedef base::Callback<void(Event event, const std::string& data)>
+      EventCallback;
+
+  HostapdMonitor(const EventCallback& callback_,
+                 const std::string& control_interface_path,
+                 const std::string& network_interface_name);
+  virtual ~HostapdMonitor();
+
+  virtual void Start();
+
+ private:
+  friend class HostapdMonitorTest;
+
+  static const char kLocalPathFormat[];
+  static const char kHostapdCmdAttach[];
+  static const char kHostapdRespOk[];
+  static const char kHostapdEventStationConnected[];
+  static const char kHostapdEventStationDisconnected[];
+  static const int kHostapdCtrlIfaceCheckIntervalMs;
+  static const int kHostapdCtrlIfaceCheckMaxAttempts;
+  static const int kHostapdAttachTimeoutMs;
+  static const int kInvalidSocket;
+
+  // Task for checking if hostapd control interface is up or not.
+  void HostapdCtrlIfaceCheckTask();
+
+  // Attach to hostapd control interface to receive unsolicited event
+  // notifications.
+  void AttachToHostapd();
+  void AttachTimeoutHandler();
+
+  bool SendMessage(const char* message, size_t length);
+  void ParseMessage(shill::InputData* data);
+  void OnReadError(const std::string& error_msg);
+
+  std::unique_ptr<shill::Sockets> sockets_;
+  EventCallback event_callback_;
+
+  // File path for interprocess communication with hostapd.
+  std::string dest_path_;
+  std::string local_path_;
+
+  // Socket descriptor for communication with hostapd.
+  int hostapd_socket_;
+
+  base::Callback<void(shill::InputData *)> hostapd_callback_;
+  std::unique_ptr<shill::IOHandler> hostapd_input_handler_;
+  shill::IOHandlerFactory *io_handler_factory_;
+  EventDispatcher* event_dispatcher_;
+  base::WeakPtrFactory<HostapdMonitor> weak_ptr_factory_;
+
+  int hostapd_ctrl_iface_check_count_;
+  base::CancelableClosure attach_timeout_callback_;
+
+  bool started_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostapdMonitor);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_HOSTAPD_MONITOR_H_
diff --git a/hostapd_monitor_unittest.cc b/hostapd_monitor_unittest.cc
new file mode 100644
index 0000000..44f0304
--- /dev/null
+++ b/hostapd_monitor_unittest.cc
@@ -0,0 +1,104 @@
+// 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 "apmanager/hostapd_monitor.h"
+
+#include <base/bind.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <shill/net/io_handler.h>
+
+#include "apmanager/mock_event_dispatcher.h"
+
+using base::Bind;
+using base::Unretained;
+using ::testing::_;
+
+namespace {
+  const char kStationMac[] = "00:11:22:33:44:55";
+  const char kHostapdEventStationConnected[] =
+      "<2>AP-STA-CONNECTED 00:11:22:33:44:55";
+  const char kHostapdEventStationDisconnected[] =
+      "<2>AP-STA-DISCONNECTED 00:11:22:33:44:55";
+}  // namespace
+
+namespace apmanager {
+
+class HostapdEventCallbackObserver {
+ public:
+  HostapdEventCallbackObserver()
+      : event_callback_(
+          Bind(&HostapdEventCallbackObserver::OnEventCallback,
+               Unretained(this))) {}
+  virtual ~HostapdEventCallbackObserver() {}
+
+  MOCK_METHOD2(OnEventCallback,
+               void(HostapdMonitor::Event event, const std::string& data));
+
+  const HostapdMonitor::EventCallback event_callback() {
+    return event_callback_;
+  }
+
+ private:
+  HostapdMonitor::EventCallback event_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostapdEventCallbackObserver);
+};
+
+class HostapdMonitorTest : public testing::Test {
+ public:
+  HostapdMonitorTest()
+      : hostapd_monitor_(observer_.event_callback(), "", ""),
+        event_dispatcher_(MockEventDispatcher::GetInstance()) {}
+
+  virtual void SetUp() {
+    hostapd_monitor_.event_dispatcher_ = event_dispatcher_;
+  }
+
+  void Start() {
+    hostapd_monitor_.Start();
+  }
+
+  void ParseMessage(shill::InputData* data) {
+    hostapd_monitor_.ParseMessage(data);
+  }
+
+ protected:
+  HostapdEventCallbackObserver observer_;
+  HostapdMonitor hostapd_monitor_;
+  MockEventDispatcher* event_dispatcher_;
+};
+
+TEST_F(HostapdMonitorTest, Start) {
+  EXPECT_CALL(*event_dispatcher_, PostTask(_)).Times(1);
+  Start();
+
+  // Monitor already started, nothing to be done.
+  EXPECT_CALL(*event_dispatcher_, PostTask(_)).Times(0);
+  Start();
+}
+
+TEST_F(HostapdMonitorTest, StationConnected) {
+  shill::InputData data;
+  data.buf = reinterpret_cast<unsigned char*>(
+      const_cast<char*>(kHostapdEventStationConnected));
+  data.len = strlen(kHostapdEventStationConnected);
+  EXPECT_CALL(observer_,
+              OnEventCallback(HostapdMonitor::kStationConnected,
+                              kStationMac)).Times(1);
+  ParseMessage(&data);
+}
+
+TEST_F(HostapdMonitorTest, StationDisconnected) {
+  shill::InputData data;
+  data.buf = reinterpret_cast<unsigned char*>(
+      const_cast<char*>(kHostapdEventStationDisconnected));
+  data.len = strlen(kHostapdEventStationDisconnected);
+  EXPECT_CALL(observer_,
+              OnEventCallback(HostapdMonitor::kStationDisconnected,
+                              kStationMac)).Times(1);
+  ParseMessage(&data);
+}
+
+}  // namespace apmanager
diff --git a/init/apmanager-seccomp-amd64.policy b/init/apmanager-seccomp-amd64.policy
new file mode 100644
index 0000000..b59bb06
--- /dev/null
+++ b/init/apmanager-seccomp-amd64.policy
@@ -0,0 +1,89 @@
+# Tested on stumpy board
+getegid: 1
+geteuid: 1
+getgid: 1
+getpid: 1
+getresgid: 1
+getresuid: 1
+gettid: 1
+getuid: 1
+setgroups: 1
+setresgid: 1
+setresuid: 1
+
+clock_getres: 1
+clock_gettime: 1
+nanosleep: 1
+alarm: 1
+
+connect: 1
+bind: 1
+getsockname: 1
+pipe: 1
+recvfrom: 1
+recvmsg: 1
+sendmsg: 1
+select: 1
+sendto: 1
+setsockopt: 1
+socket: 1
+socketpair: 1
+
+close: 1
+creat: 1
+ioctl: 1
+open: 1
+prctl: 1
+read: 1
+write: 1
+arch_prctl: 1
+capget: 1
+
+brk: 1
+dup2: 1
+clone: 1
+fork: 1
+mmap: 1
+munmap: 1
+
+fcntl: 1
+fstat: 1
+fsync: 1
+ftruncate: 1
+lseek: 1
+stat: 1
+
+futex: 1
+
+exit: 1
+exit_group: 1
+kill: 1
+rt_sigaction: 1
+rt_sigprocmask: 1
+rt_sigreturn: 1
+signalfd4: 1
+tkill: 1
+
+epoll_create: 1
+epoll_ctl: 1
+epoll_wait: 1
+poll: 1
+wait4: 1
+
+chdir: 1
+readlink: 1
+umask: 1
+
+set_robust_list: 1
+set_tid_address: 1
+
+execve: 1
+mprotect: 1
+access: 1
+getrlimit: 1
+unlink: 1
+mkdir: 1
+rmdir: 1
+chown: 1
+chmod: 1
+writev: 1
\ No newline at end of file
diff --git a/init/apmanager-seccomp-arm.policy b/init/apmanager-seccomp-arm.policy
new file mode 100644
index 0000000..7a7f6ce
--- /dev/null
+++ b/init/apmanager-seccomp-arm.policy
@@ -0,0 +1,84 @@
+# Tested on peach_pit board
+socket: 1
+setsockopt: 1
+bind: 1
+clock_gettime: 1
+_newselect: 1
+recvfrom: 1
+epoll_ctl: 1
+gettid: 1
+write: 1
+epoll_wait: 1
+read: 1
+open: 1
+futex: 1
+brk: 1
+fstat64: 1
+mmap2: 1
+close: 1
+munmap: 1
+sendmsg: 1
+poll: 1
+recvmsg: 1
+fork: 1
+clone: 1
+ioctl: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_sigreturn: 1
+sigreturn: 1
+connect: 1
+sendto: 1
+creat: 1
+access: 1
+set_robust_list: 1
+set_tid_address: 1
+wait4: 1
+exit: 1
+exit_group: 1
+epoll_create: 1
+
+fcntl64: 1
+prctl: 1
+capget: 1
+capset: 1
+dup2: 1
+
+getpid: 1
+getuid32: 1
+setgroups32: 1
+setresgid32: 1
+setresuid32: 1
+setresgid32: 1
+setresuid32: 1
+setitimer: 1
+mprotect: 1
+stat64: 1
+send: 1
+_llseek: 1
+signalfd4: 1
+execve: 1
+
+getsockname: 1
+readlink: 1
+gettimeofday: 1
+
+restart_syscall: 1
+uname: 1
+ARM_set_tls: 1
+ugetrlimit: 1
+kill: 1
+nanosleep: 1
+
+umask: 1
+pipe: 1
+chdir: 1
+ftruncate64: 1
+fsync: 1
+unlink: 1
+mkdir: 1
+rmdir: 1
+chmod: 1
+chown32: 1
+dup: 1
+writev: 1
\ No newline at end of file
diff --git a/init/apmanager-seccomp-mips.policy b/init/apmanager-seccomp-mips.policy
new file mode 100644
index 0000000..1f3bda9
--- /dev/null
+++ b/init/apmanager-seccomp-mips.policy
@@ -0,0 +1,69 @@
+socket: 1
+connect: 1
+setsockopt: 1
+bind: 1
+clock_gettime: 1
+_newselect: 1
+recvfrom: 1
+epoll_ctl: 1
+send: 1
+gettid: 1
+write: 1
+gettimeofday: 1
+epoll_wait: 1
+read: 1
+time: 1
+open: 1
+brk: 1
+fstat64: 1
+mmap: 1
+close: 1
+munmap: 1
+sendmsg: 1
+poll: 1
+recvmsg: 1
+clone: 1
+futex: 1
+ioctl: 1
+fcntl64: 1
+stat64: 1
+set_robust_list: 1
+rt_sigprocmask: 1
+execve: 1
+access: 1
+uname: 1
+lseek: 1
+set_thread_area: 1
+mprotect: 1
+set_tid_address: 1
+getrlimit: 1
+rt_sigaction: 1
+getsockname: 1
+umask: 1
+_llseek: 1
+nanosleep: 1
+restart_syscall: 1
+readlink: 1
+sendto: 1
+mkdir: 1
+capget: 1
+chown: 1
+pipe: 1
+chdir: 1
+chmod: 1
+getuid: 1
+unlink: 1
+dup2: 1
+getpid: 1
+munmap: 1
+rmdir: 1
+exit_group: 1
+ftruncate64: 1
+fsync: 1
+alarm: 1
+signalfd4: 1
+sigreturn: 1
+kill: 1
+rt_sigaction: 1
+waitpid: 1
+writev: 1
\ No newline at end of file
diff --git a/init/apmanager-seccomp-x86.policy b/init/apmanager-seccomp-x86.policy
new file mode 100644
index 0000000..aaf206a
--- /dev/null
+++ b/init/apmanager-seccomp-x86.policy
@@ -0,0 +1,71 @@
+# Tested on x86-alex board
+clock_gettime: 1
+_newselect: 1
+epoll_ctl: 1
+gettid: 1
+write: 1
+gettimeofday: 1
+epoll_wait: 1
+read: 1
+open: 1
+brk: 1
+fstat64: 1
+mmap2: 1
+close: 1
+munmap: 1
+poll: 1
+rt_sigprocmask: 1
+clone: 1
+signalfd4: 1
+ioctl: 1
+set_robust_list: 1
+fork: 1
+stat64: 1
+execve: 1
+kill: 1
+fcntl64: 1
+access: 1
+mprotect: 1
+waitpid: 1
+set_thread_area: 1
+set_tid_address: 1
+futex: 1
+rt_sigaction: 1
+ugetrlimit: 1
+uname: 1
+readlink: 1
+nanosleep: 1
+restart_syscall: 1
+exit_group: 1
+alarm: 1
+sigreturn: 1
+umask: 1
+_llseek: 1
+capget: 1
+pipe: 1
+chdir: 1
+getuid32: 1
+dup2: 1
+getpid: 1
+stat64: 1
+ftruncate64: 1
+fsync: 1
+prctl: 1
+capset: 1
+getresgid32: 1
+getresuid32: 1
+geteuid32: 1
+getgid32: 1
+getegid32: 1
+setresgid32: 1
+setresuid32: 1
+tgkill: 1
+time: 1
+epoll_create: 1
+socketcall: 1
+mkdir: 1
+rmdir: 1
+chown32: 1
+chmod: 1
+unlink: 1
+writev: 1
\ No newline at end of file
diff --git a/init/apmanager.conf b/init/apmanager.conf
new file mode 100644
index 0000000..3d6f886
--- /dev/null
+++ b/init/apmanager.conf
@@ -0,0 +1,31 @@
+# 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.
+
+description   "Run the access point manager daemon"
+author        "chromium-os-dev@chromium.org"
+
+start on stopped iptables and stopped ip6tables and started shill
+stop on stopping system-services
+expect fork
+
+env APMANAGER_LOG_LEVEL=0
+
+pre-start script
+  # Load the module that provides the WiFi configuration API, since
+  # apmanager will abort if that API is not available. In most cases,
+  # cfg80211 will be loaded implicitly when the device driver is
+  # loaded (in preload-network).  However, this deals with the
+  # first-boot case, in case apmanager starts before the device driver is
+  # loaded.
+  modprobe cfg80211 ||
+    logger -p err -t "$UPSTART_JOB" "Failed to load cfg80211"
+
+  # Create directory for storing config files.
+  mkdir -m 0755 -p /var/run/apmanager/hostapd
+  mkdir -m 0755 -p /var/run/apmanager/dnsmasq
+  chown -R apmanager:apmanager /var/run/apmanager/hostapd
+  chown -R apmanager:apmanager /var/run/apmanager/dnsmasq
+end script
+
+exec /usr/bin/apmanager --v="${APMANAGER_LOG_LEVEL}"
diff --git a/main.cc b/main.cc
new file mode 100644
index 0000000..baac20b
--- /dev/null
+++ b/main.cc
@@ -0,0 +1,129 @@
+// 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 <vector>
+
+#include <base/bind.h>
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <chromeos/minijail/minijail.h>
+#include <chromeos/syslog_logging.h>
+
+#include "apmanager/daemon.h"
+
+using std::vector;
+
+namespace {
+
+namespace switches {
+
+// Don't daemon()ize; run in foreground.
+const char kForeground[] = "foreground";
+// Flag that causes apmanager to show the help message and exit.
+const char kHelp[] = "help";
+
+// The help message shown if help flag is passed to the program.
+const char kHelpMessage[] = "\n"
+    "Available Switches: \n"
+    "  --foreground\n"
+    "    Don\'t daemon()ize; run in foreground.\n";
+}  // namespace switches
+
+}  // namespace
+
+namespace {
+
+const char kLoggerCommand[] = "/usr/bin/logger";
+const char kLoggerUser[] = "syslog";
+const char kSeccompFilePath[] = "/usr/share/policy/apmanager-seccomp.policy";
+
+}  // namespace
+
+// Always logs to the syslog and logs to stderr if
+// we are running in the foreground.
+void SetupLogging(chromeos::Minijail* minijail,
+                  bool foreground,
+                  const char* daemon_name) {
+  int log_flags = 0;
+  log_flags |= chromeos::kLogToSyslog;
+  log_flags |= chromeos::kLogHeader;
+  if (foreground) {
+    log_flags |= chromeos::kLogToStderr;
+  }
+  chromeos::InitLog(log_flags);
+
+  if (!foreground) {
+    vector<char*> logger_command_line;
+    int logger_stdin_fd;
+    logger_command_line.push_back(const_cast<char*>(kLoggerCommand));
+    logger_command_line.push_back(const_cast<char*>("--priority"));
+    logger_command_line.push_back(const_cast<char*>("daemon.err"));
+    logger_command_line.push_back(const_cast<char*>("--tag"));
+    logger_command_line.push_back(const_cast<char*>(daemon_name));
+    logger_command_line.push_back(nullptr);
+
+    struct minijail* jail = minijail->New();
+    minijail->DropRoot(jail, kLoggerUser, kLoggerUser);
+
+    if (!minijail->RunPipeAndDestroy(jail, logger_command_line,
+                                     nullptr, &logger_stdin_fd)) {
+      LOG(ERROR) << "Unable to spawn logger. "
+                 << "Writes to stderr will be discarded.";
+      return;
+    }
+
+    // Note that we don't set O_CLOEXEC here. This means that stderr
+    // from any child processes will, by default, be logged to syslog.
+    if (dup2(logger_stdin_fd, fileno(stderr)) != fileno(stderr)) {
+      LOG(ERROR) << "Failed to redirect stderr to syslog: "
+                 << strerror(errno);
+    }
+    close(logger_stdin_fd);
+  }
+}
+
+void DropPrivileges(chromeos::Minijail* minijail) {
+  struct minijail* jail = minijail->New();
+  minijail->DropRoot(jail, apmanager::Daemon::kAPManagerUserName,
+                     apmanager::Daemon::kAPManagerGroupName);
+  // Permissions needed for the daemon and its child processes for managing
+  // network interfaces and binding to network sockets.
+  minijail->UseCapabilities(jail, CAP_TO_MASK(CAP_NET_ADMIN) |
+                                  CAP_TO_MASK(CAP_NET_RAW) |
+                                  CAP_TO_MASK(CAP_NET_BIND_SERVICE));
+  minijail->UseSeccompFilter(jail, kSeccompFilePath);
+  minijail_enter(jail);
+  minijail->Destroy(jail);
+}
+
+void OnStartup(const char* daemon_name, base::CommandLine* cl) {
+  chromeos::Minijail* minijail = chromeos::Minijail::GetInstance();
+  SetupLogging(minijail, cl->HasSwitch(switches::kForeground), daemon_name);
+
+  LOG(INFO) << __func__ << ": Dropping privileges";
+
+  // Now that the daemon has all the resources it needs to run, we can drop
+  // privileges further.
+  DropPrivileges(minijail);
+}
+
+int main(int argc, char* argv[]) {
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
+
+  if (cl->HasSwitch(switches::kHelp)) {
+    LOG(INFO) << switches::kHelpMessage;
+    return 0;
+  }
+
+  const int nochdir = 0, noclose = 0;
+  if (!cl->HasSwitch(switches::kForeground))
+    PLOG_IF(FATAL, daemon(nochdir, noclose) == -1) << "Failed to daemonize";
+
+  apmanager::Daemon daemon(base::Bind(&OnStartup, argv[0], cl));
+
+  daemon.Run();
+
+  return 0;
+}
diff --git a/manager.cc b/manager.cc
new file mode 100644
index 0000000..959119d
--- /dev/null
+++ b/manager.cc
@@ -0,0 +1,215 @@
+// 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 "apmanager/manager.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/service_constants.h>
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::ExportedObjectManager;
+using chromeos::dbus_utils::DBusMethodResponse;
+using std::string;
+
+namespace apmanager {
+
+Manager::Manager()
+    : org::chromium::apmanager::ManagerAdaptor(this),
+      service_identifier_(0),
+      device_identifier_(0),
+      device_info_(this) {}
+
+Manager::~Manager() {
+  // Terminate all services before cleanup other resources.
+  for (auto& service : services_) {
+    service.reset();
+  }
+}
+
+void Manager::RegisterAsync(ExportedObjectManager* object_manager,
+                            const scoped_refptr<dbus::Bus>& bus,
+                            AsyncEventSequencer* sequencer) {
+  CHECK(!dbus_object_) << "Already registered";
+  dbus_object_.reset(
+      new chromeos::dbus_utils::DBusObject(
+          object_manager,
+          bus,
+          org::chromium::apmanager::ManagerAdaptor::GetObjectPath()));
+  RegisterWithDBusObject(dbus_object_.get());
+  dbus_object_->RegisterAsync(
+      sequencer->GetHandler("Manager.RegisterAsync() failed.", true));
+  bus_ = bus;
+
+  shill_proxy_.Init(bus);
+  firewall_manager_.Init(bus);
+}
+
+void Manager::Start() {
+  device_info_.Start();
+}
+
+void Manager::Stop() {
+  device_info_.Stop();
+}
+
+void Manager::CreateService(
+    std::unique_ptr<DBusMethodResponse<dbus::ObjectPath>> response,
+    dbus::Message* message) {
+  LOG(INFO) << "Manager::CreateService";
+  scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
+  std::unique_ptr<Service> service(new Service(this, service_identifier_));
+
+  service->RegisterAsync(
+      dbus_object_->GetObjectManager().get(), bus_, sequencer.get());
+  sequencer->OnAllTasksCompletedCall({
+      base::Bind(&Manager::OnServiceRegistered,
+                 base::Unretained(this),
+                 base::Passed(&response),
+                 base::Passed(&service))
+  });
+
+  base::Closure on_connection_vanish = base::Bind(
+      &Manager::OnAPServiceOwnerDisappeared,
+      base::Unretained(this),
+      service_identifier_);
+  service_watchers_[service_identifier_].reset(
+      new DBusServiceWatcher{bus_, message->GetSender(), on_connection_vanish});
+  service_identifier_++;
+}
+
+bool Manager::RemoveService(chromeos::ErrorPtr* error,
+                            dbus::Message* message,
+                            const dbus::ObjectPath& in_service) {
+  for (auto it = services_.begin(); it != services_.end(); ++it) {
+    if ((*it)->dbus_path() == in_service) {
+      // Verify the owner.
+      auto watcher = service_watchers_.find((*it)->identifier());
+      CHECK(watcher != service_watchers_.end())
+          << "DBus watcher not created for service: " << (*it)->identifier();
+      if (watcher->second->connection_name() != message->GetSender()) {
+        chromeos::Error::AddToPrintf(
+            error, FROM_HERE, chromeos::errors::dbus::kDomain, kManagerError,
+            "Service %d is owned by another local process.",
+            (*it)->identifier());
+        return false;
+      }
+      service_watchers_.erase(watcher);
+
+      services_.erase(it);
+      return true;
+    }
+  }
+
+  chromeos::Error::AddTo(
+      error, FROM_HERE, chromeos::errors::dbus::kDomain, kManagerError,
+      "Service does not exist");
+  return false;
+}
+
+scoped_refptr<Device> Manager::GetAvailableDevice() {
+  for (const auto& device : devices_) {
+    // Look for an unused device with AP interface mode support.
+    if (!device->GetInUsed() && !device->GetPreferredApInterface().empty()) {
+      return device;
+    }
+  }
+  return nullptr;
+}
+
+scoped_refptr<Device> Manager::GetDeviceFromInterfaceName(
+    const string& interface_name) {
+  for (const auto& device : devices_) {
+    if (device->InterfaceExists(interface_name)) {
+      return device;
+    }
+  }
+  return nullptr;
+}
+
+void Manager::RegisterDevice(scoped_refptr<Device> device) {
+  LOG(INFO) << "Manager::RegisterDevice: registering device "
+            << device->GetDeviceName();
+  // Register device DBbus interfaces.
+  scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
+  device->RegisterAsync(dbus_object_->GetObjectManager().get(),
+                        bus_,
+                        sequencer.get(),
+                        device_identifier_++);
+  sequencer->OnAllTasksCompletedCall({
+    base::Bind(&Manager::OnDeviceRegistered,
+               base::Unretained(this),
+               device)
+  });
+}
+
+void Manager::ClaimInterface(const string& interface_name) {
+  shill_proxy_.ClaimInterface(interface_name);
+}
+
+void Manager::ReleaseInterface(const string& interface_name) {
+  shill_proxy_.ReleaseInterface(interface_name);
+}
+
+void Manager::RequestDHCPPortAccess(const string& interface) {
+  firewall_manager_.RequestDHCPPortAccess(interface);
+}
+
+void Manager::ReleaseDHCPPortAccess(const string& interface) {
+  firewall_manager_.ReleaseDHCPPortAccess(interface);
+}
+
+void Manager::OnServiceRegistered(
+    std::unique_ptr<DBusMethodResponse<dbus::ObjectPath>> response,
+    std::unique_ptr<Service> service,
+    bool success) {
+  LOG(INFO) << "ServiceRegistered";
+  // Success should always be true since we've said that failures are fatal.
+  CHECK(success) << "Init of one or more objects has failed.";
+
+  // Remove this service if the owner doesn't exist anymore. It is theoretically
+  // possible to have the owner disappear before the AP service complete its
+  // registration with DBus.
+  if (service_watchers_.find(service->identifier()) ==
+      service_watchers_.end()) {
+    LOG(INFO) << "Service " << service->identifier()
+              << ": owner doesn't exist anymore";
+    service.reset();
+    return;
+  }
+
+  // Add service to the service list and return the service dbus path for the
+  // CreateService call.
+  dbus::ObjectPath service_path = service->dbus_path();
+  services_.push_back(std::move(service));
+  response->Return(service_path);
+}
+
+void Manager::OnDeviceRegistered(scoped_refptr<Device> device, bool success) {
+  // Success should always be true since we've said that failures are fatal.
+  CHECK(success) << "Init of one or more objects has failed.";
+
+  devices_.push_back(device);
+  // TODO(zqiu): Property update for available devices.
+}
+
+void Manager::OnAPServiceOwnerDisappeared(int service_identifier) {
+  LOG(INFO) << "Owner for service " << service_identifier << " disappeared";
+  // Remove service watcher.
+  auto watcher = service_watchers_.find(service_identifier);
+  CHECK(watcher != service_watchers_.end())
+      << "Owner disappeared without watcher setup";
+  service_watchers_.erase(watcher);
+
+  // Remove the service.
+  for (auto it = services_.begin(); it != services_.end(); ++it) {
+    if ((*it)->identifier() == service_identifier) {
+      services_.erase(it);
+      return;
+    }
+  }
+  LOG(INFO) << "Owner for service " << service_identifier
+            << " disappeared before it is registered";
+}
+
+}  // namespace apmanager
diff --git a/manager.h b/manager.h
new file mode 100644
index 0000000..9c5fb93
--- /dev/null
+++ b/manager.h
@@ -0,0 +1,111 @@
+// 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 APMANAGER_MANAGER_H_
+#define APMANAGER_MANAGER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <chromeos/dbus/dbus_service_watcher.h>
+
+#include "apmanager/dbus_adaptors/org.chromium.apmanager.Manager.h"
+
+#include "apmanager/device_info.h"
+#include "apmanager/firewall_manager.h"
+#include "apmanager/service.h"
+#include "apmanager/shill_proxy.h"
+
+namespace apmanager {
+
+class Manager : public org::chromium::apmanager::ManagerAdaptor,
+                public org::chromium::apmanager::ManagerInterface {
+ public:
+  template<typename T>
+  using DBusMethodResponse = chromeos::dbus_utils::DBusMethodResponse<T>;
+
+  Manager();
+  virtual ~Manager();
+
+  // Implementation of ManagerInterface.
+  // Handles calls to org.chromium.apmanager.Manager.CreateService().
+  // This is an asynchronous call, response is invoked when Service and Config
+  // dbus objects complete the DBus service registration.
+  virtual void CreateService(
+      std::unique_ptr<DBusMethodResponse<dbus::ObjectPath>> response,
+      dbus::Message* message);
+  // Handles calls to org.chromium.apmanager.Manager.RemoveService().
+  virtual bool RemoveService(chromeos::ErrorPtr* error,
+                             dbus::Message* message,
+                             const dbus::ObjectPath& in_service);
+
+  // Register DBus object.
+  void RegisterAsync(
+      chromeos::dbus_utils::ExportedObjectManager* object_manager,
+      const scoped_refptr<dbus::Bus>& bus,
+      chromeos::dbus_utils::AsyncEventSequencer* sequencer);
+
+  virtual void Start();
+  virtual void Stop();
+
+  virtual void RegisterDevice(scoped_refptr<Device> device);
+
+  // Return an unuse device with AP interface mode support.
+  virtual scoped_refptr<Device> GetAvailableDevice();
+
+  // Return the device that's associated with the given interface
+  // |interface_name|.
+  virtual scoped_refptr<Device> GetDeviceFromInterfaceName(
+      const std::string& interface_name);
+
+  // Claim the given interface |interface_name| from shill.
+  virtual void ClaimInterface(const std::string& interface_name);
+  // Release the given interface |interface_name| to shill.
+  virtual void ReleaseInterface(const std::string& interface_name);
+
+  // Request/release access to DHCP port for the specified interface.
+  virtual void RequestDHCPPortAccess(const std::string& interface);
+  virtual void ReleaseDHCPPortAccess(const std::string& interface);
+
+ private:
+  friend class ManagerTest;
+
+  // A callback that will be called when the Service/Config D-Bus
+  // objects/interfaces are exported successfully and ready to be used.
+  void OnServiceRegistered(
+      std::unique_ptr<DBusMethodResponse<dbus::ObjectPath>> response,
+      std::unique_ptr<Service> service,
+      bool success);
+
+  // A callback that will be called when a Device D-Bus object/interface is
+  // exported successfully and ready to be used.
+  void OnDeviceRegistered(scoped_refptr<Device> device, bool success);
+
+  // This is invoked when the owner of an AP service disappeared.
+  void OnAPServiceOwnerDisappeared(int service_identifier);
+
+  int service_identifier_;
+  int device_identifier_;
+  std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+  scoped_refptr<dbus::Bus> bus_;
+  std::vector<std::unique_ptr<Service>> services_;
+  std::vector<scoped_refptr<Device>> devices_;
+  // DBus service watchers for the owner of AP services.
+  using DBusServiceWatcher = chromeos::dbus_utils::DBusServiceWatcher;
+  std::map<int, std::unique_ptr<DBusServiceWatcher>> service_watchers_;
+  DeviceInfo device_info_;
+
+  // Proxy to shill DBus services.
+  ShillProxy shill_proxy_;
+  // Proxy to DBus service for managing firewall rules.
+  FirewallManager firewall_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(Manager);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MANAGER_H_
diff --git a/manager_unittest.cc b/manager_unittest.cc
new file mode 100644
index 0000000..5d7e5a4
--- /dev/null
+++ b/manager_unittest.cc
@@ -0,0 +1,87 @@
+// 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 "apmanager/manager.h"
+
+#include <gtest/gtest.h>
+
+#include "apmanager/mock_device.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace apmanager {
+
+class ManagerTest : public testing::Test {
+ public:
+  ManagerTest() : manager_() {}
+
+  void RegisterDevice(scoped_refptr<Device> device) {
+    manager_.devices_.push_back(device);
+  }
+
+ protected:
+  Manager manager_;
+};
+
+TEST_F(ManagerTest, GetAvailableDevice) {
+  // Register a device without AP support (no preferred AP interface).
+  scoped_refptr<MockDevice> device0 = new MockDevice();
+  RegisterDevice(device0);
+
+  // No available device for AP operation.
+  EXPECT_EQ(nullptr, manager_.GetAvailableDevice());
+
+  // Add AP support to the device.
+  const char kTestInterface0[] = "test-interface0";
+  device0->SetPreferredApInterface(kTestInterface0);
+  EXPECT_EQ(device0, manager_.GetAvailableDevice());
+
+  // Register another device with AP support.
+  const char kTestInterface1[] = "test-interface1";
+  scoped_refptr<MockDevice> device1 = new MockDevice();
+  device1->SetPreferredApInterface(kTestInterface1);
+  RegisterDevice(device1);
+
+  // Both devices are idle by default, should return the first added device.
+  EXPECT_EQ(device0, manager_.GetAvailableDevice());
+
+  // Set first one to be in used, should return the non-used device.
+  device0->SetInUsed(true);
+  EXPECT_EQ(device1, manager_.GetAvailableDevice());
+
+  // Both devices are in used, should return a nullptr.
+  device1->SetInUsed(true);
+  EXPECT_EQ(nullptr, manager_.GetAvailableDevice());
+}
+
+TEST_F(ManagerTest, GetDeviceFromInterfaceName) {
+  // Register two devices
+  scoped_refptr<MockDevice> device0 = new MockDevice();
+  scoped_refptr<MockDevice> device1 = new MockDevice();
+  RegisterDevice(device0);
+  RegisterDevice(device1);
+
+  const char kTestInterface0[] = "test-interface0";
+  const char kTestInterface1[] = "test-interface1";
+
+  // interface0 belongs to device0.
+  EXPECT_CALL(*device0.get(), InterfaceExists(kTestInterface0))
+      .WillOnce(Return(true));
+  EXPECT_EQ(device0, manager_.GetDeviceFromInterfaceName(kTestInterface0));
+
+  // interface1 belongs to device1.
+  EXPECT_CALL(*device0.get(), InterfaceExists(_))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(*device1.get(), InterfaceExists(kTestInterface1))
+      .WillOnce(Return(true));
+  EXPECT_EQ(device1, manager_.GetDeviceFromInterfaceName(kTestInterface1));
+
+  // "random" interface is not found.
+  EXPECT_CALL(*device1.get(), InterfaceExists(_))
+      .WillRepeatedly(Return(false));
+  EXPECT_EQ(nullptr, manager_.GetDeviceFromInterfaceName("random"));
+}
+
+}  // namespace apmanager
diff --git a/mock_config.cc b/mock_config.cc
new file mode 100644
index 0000000..d1f27ca
--- /dev/null
+++ b/mock_config.cc
@@ -0,0 +1,13 @@
+// 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 "apmanager/mock_config.h"
+
+namespace apmanager {
+
+MockConfig::MockConfig() : Config(nullptr, std::string()) {}
+
+MockConfig::~MockConfig() {}
+
+}  // namespace apmanager
diff --git a/mock_config.h b/mock_config.h
new file mode 100644
index 0000000..4f1abc9
--- /dev/null
+++ b/mock_config.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 APMANAGER_MOCK_CONFIG_H_
+#define APMANAGER_MOCK_CONFIG_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/config.h"
+
+namespace apmanager {
+
+class MockConfig : public Config {
+ public:
+  MockConfig();
+  ~MockConfig() override;
+
+  MOCK_METHOD2(GenerateConfigFile,
+               bool(chromeos::ErrorPtr *error,
+                    std::string* config_str));
+  MOCK_METHOD0(ClaimDevice, bool());
+  MOCK_METHOD0(ReleaseDevice, bool());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockConfig);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_CONFIG_H_
diff --git a/mock_device.cc b/mock_device.cc
new file mode 100644
index 0000000..770c2bd
--- /dev/null
+++ b/mock_device.cc
@@ -0,0 +1,13 @@
+// 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 "apmanager/mock_device.h"
+
+namespace apmanager {
+
+MockDevice::MockDevice() : Device(nullptr, "") {}
+
+MockDevice::~MockDevice() {}
+
+}  // namespace apmanager
diff --git a/mock_device.h b/mock_device.h
new file mode 100644
index 0000000..31f2944
--- /dev/null
+++ b/mock_device.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 APMANAGER_MOCK_DEVICE_H_
+#define APMANAGER_MOCK_DEVICE_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/device.h"
+
+namespace apmanager {
+
+class MockDevice : public Device {
+ public:
+  MockDevice();
+  ~MockDevice() override;
+
+  MOCK_METHOD1(RegisterInterface,
+               void(const WiFiInterface& interface));
+  MOCK_METHOD1(DeregisterInterface,
+               void(const WiFiInterface& interface));
+  MOCK_METHOD1(ParseWiphyCapability,
+               void(const shill::Nl80211Message& msg));
+  MOCK_METHOD1(ClaimDevice, bool(bool full_control));
+  MOCK_METHOD0(ReleaseDevice, bool());
+  MOCK_METHOD1(InterfaceExists, bool(const std::string& interface_name));
+  MOCK_METHOD2(GetHTCapability, bool(uint16_t channel, std::string* ht_capab));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDevice);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_DEVICE_H_
diff --git a/mock_dhcp_server.cc b/mock_dhcp_server.cc
new file mode 100644
index 0000000..de31eae
--- /dev/null
+++ b/mock_dhcp_server.cc
@@ -0,0 +1,13 @@
+// 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 "apmanager/mock_dhcp_server.h"
+
+namespace apmanager {
+
+MockDHCPServer::MockDHCPServer() : DHCPServer(0, "") {}
+
+MockDHCPServer::~MockDHCPServer() {}
+
+}  // namespace apmanager
diff --git a/mock_dhcp_server.h b/mock_dhcp_server.h
new file mode 100644
index 0000000..df68b81
--- /dev/null
+++ b/mock_dhcp_server.h
@@ -0,0 +1,28 @@
+// 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 APMANAGER_MOCK_DHCP_SERVER_H_
+#define APMANAGER_MOCK_DHCP_SERVER_H_
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/dhcp_server.h"
+
+namespace apmanager {
+
+class MockDHCPServer : public DHCPServer {
+ public:
+  MockDHCPServer();
+  ~MockDHCPServer() override;
+
+  MOCK_METHOD0(Start, bool());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDHCPServer);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_DHCP_SERVER_H_
diff --git a/mock_dhcp_server_factory.cc b/mock_dhcp_server_factory.cc
new file mode 100644
index 0000000..b5eb7a4
--- /dev/null
+++ b/mock_dhcp_server_factory.cc
@@ -0,0 +1,21 @@
+// 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 "apmanager/mock_dhcp_server_factory.h"
+
+namespace apmanager {
+
+namespace {
+base::LazyInstance<MockDHCPServerFactory> g_mock_dhcp_server_factory
+    = LAZY_INSTANCE_INITIALIZER;
+}  // namespace
+
+MockDHCPServerFactory::MockDHCPServerFactory() {}
+MockDHCPServerFactory::~MockDHCPServerFactory() {}
+
+MockDHCPServerFactory* MockDHCPServerFactory::GetInstance() {
+  return g_mock_dhcp_server_factory.Pointer();
+}
+
+}  // namespace apmanager
diff --git a/mock_dhcp_server_factory.h b/mock_dhcp_server_factory.h
new file mode 100644
index 0000000..c20fd59
--- /dev/null
+++ b/mock_dhcp_server_factory.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 APMANAGER_MOCK_DHCP_SERVER_FACTORY_H_
+#define APMANAGER_MOCK_DHCP_SERVER_FACTORY_H_
+
+#include <string>
+
+#include <base/lazy_instance.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/dhcp_server_factory.h"
+
+namespace apmanager {
+
+class MockDHCPServerFactory : public DHCPServerFactory {
+ public:
+  ~MockDHCPServerFactory() override;
+
+  // This is a singleton. Use MockDHCPServerFactory::GetInstance()->Foo().
+  static MockDHCPServerFactory* GetInstance();
+
+  MOCK_METHOD2(CreateDHCPServer,
+               DHCPServer*(uint16_t server_address_index,
+                           const std::string& interface_name));
+
+ protected:
+  MockDHCPServerFactory();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<MockDHCPServerFactory>;
+
+  DISALLOW_COPY_AND_ASSIGN(MockDHCPServerFactory);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_DHCP_SERVER_FACTORY_H_
diff --git a/mock_event_dispatcher.cc b/mock_event_dispatcher.cc
new file mode 100644
index 0000000..92e739b
--- /dev/null
+++ b/mock_event_dispatcher.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 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 "apmanager/mock_event_dispatcher.h"
+
+namespace apmanager {
+
+namespace {
+base::LazyInstance<MockEventDispatcher> g_mock_event_dispatcher
+    = LAZY_INSTANCE_INITIALIZER;
+}  // namespace
+
+MockEventDispatcher::MockEventDispatcher() {}
+MockEventDispatcher::~MockEventDispatcher() {}
+
+MockEventDispatcher* MockEventDispatcher::GetInstance() {
+  return g_mock_event_dispatcher.Pointer();
+}
+
+}  // namespace apmanager
diff --git a/mock_event_dispatcher.h b/mock_event_dispatcher.h
new file mode 100644
index 0000000..aa2ba32
--- /dev/null
+++ b/mock_event_dispatcher.h
@@ -0,0 +1,37 @@
+// Copyright 2015 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 APMANAGER_MOCK_EVENT_DISPATCHER_H_
+#define APMANAGER_MOCK_EVENT_DISPATCHER_H_
+
+#include <base/lazy_instance.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/event_dispatcher.h"
+
+namespace apmanager {
+
+class MockEventDispatcher : public EventDispatcher {
+ public:
+  ~MockEventDispatcher() override;
+
+  // This is a singleton. Use MockEventDispatcher::GetInstance()->Foo().
+  static MockEventDispatcher* GetInstance();
+
+  MOCK_METHOD1(PostTask, bool(const base::Closure& task));
+  MOCK_METHOD2(PostDelayedTask, bool(const base::Closure& task,
+                                     int64_t delay_ms));
+
+ protected:
+  MockEventDispatcher();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<MockEventDispatcher>;
+
+  DISALLOW_COPY_AND_ASSIGN(MockEventDispatcher);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_EVENT_DISPATCHER_H_
diff --git a/mock_file_writer.cc b/mock_file_writer.cc
new file mode 100644
index 0000000..168cae3
--- /dev/null
+++ b/mock_file_writer.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 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 "apmanager/mock_file_writer.h"
+
+namespace apmanager {
+
+namespace {
+base::LazyInstance<MockFileWriter> g_mock_file_writer
+    = LAZY_INSTANCE_INITIALIZER;
+}  // namespace
+
+MockFileWriter::MockFileWriter() {}
+MockFileWriter::~MockFileWriter() {}
+
+MockFileWriter* MockFileWriter::GetInstance() {
+  return g_mock_file_writer.Pointer();
+}
+
+}  // namespace apmanager
diff --git a/mock_file_writer.h b/mock_file_writer.h
new file mode 100644
index 0000000..f5a7f31
--- /dev/null
+++ b/mock_file_writer.h
@@ -0,0 +1,38 @@
+// Copyright 2015 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 APMANAGER_MOCK_FILE_WRITER_H_
+#define APMANAGER_MOCK_FILE_WRITER_H_
+
+#include <string>
+
+#include <base/lazy_instance.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/file_writer.h"
+
+namespace apmanager {
+
+class MockFileWriter : public FileWriter {
+ public:
+  ~MockFileWriter() override;
+
+  // This is a singleton. Use MockFileWriter::GetInstance()->Foo().
+  static MockFileWriter* GetInstance();
+
+  MOCK_METHOD2(Write, bool(const std::string& file_name,
+                           const std::string& content));
+
+ protected:
+  MockFileWriter();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<MockFileWriter>;
+
+  DISALLOW_COPY_AND_ASSIGN(MockFileWriter);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_FILE_WRITER_H_
diff --git a/mock_hostapd_monitor.cc b/mock_hostapd_monitor.cc
new file mode 100644
index 0000000..5b3446f
--- /dev/null
+++ b/mock_hostapd_monitor.cc
@@ -0,0 +1,13 @@
+// Copyright 2015 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 "apmanager/mock_hostapd_monitor.h"
+
+namespace apmanager {
+
+MockHostapdMonitor::MockHostapdMonitor()
+    : HostapdMonitor(EventCallback(), "", "") {}
+MockHostapdMonitor::~MockHostapdMonitor() {}
+
+}  // namespace apmanager
diff --git a/mock_hostapd_monitor.h b/mock_hostapd_monitor.h
new file mode 100644
index 0000000..1f4859c
--- /dev/null
+++ b/mock_hostapd_monitor.h
@@ -0,0 +1,27 @@
+// Copyright 2015 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 APMANAGER_MOCK_HOSTAPD_MONITOR_H_
+#define APMANAGER_MOCK_HOSTAPD_MONITOR_H_
+
+#include <gmock/gmock.h>
+
+#include "apmanager/hostapd_monitor.h"
+
+namespace apmanager {
+
+class MockHostapdMonitor : public HostapdMonitor {
+ public:
+  MockHostapdMonitor();
+  ~MockHostapdMonitor() override;
+
+  MOCK_METHOD0(Start, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockHostapdMonitor);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_HOSTAPD_MONITOR_H_
diff --git a/mock_manager.cc b/mock_manager.cc
new file mode 100644
index 0000000..de61d73
--- /dev/null
+++ b/mock_manager.cc
@@ -0,0 +1,13 @@
+// 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 "apmanager/mock_manager.h"
+
+namespace apmanager {
+
+MockManager::MockManager() : Manager() {}
+
+MockManager::~MockManager() {}
+
+}  // namespace apmanager
diff --git a/mock_manager.h b/mock_manager.h
new file mode 100644
index 0000000..dbbc731
--- /dev/null
+++ b/mock_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 APMANAGER_MOCK_MANAGER_H_
+#define APMANAGER_MOCK_MANAGER_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/manager.h"
+
+namespace apmanager {
+
+class MockManager : public Manager {
+ public:
+  MockManager();
+  ~MockManager() override;
+
+  MOCK_METHOD0(Start, void());
+  MOCK_METHOD0(Stop, void());
+  MOCK_METHOD1(RegisterDevice, void(scoped_refptr<Device> device));
+  MOCK_METHOD0(GetAvailableDevice, scoped_refptr<Device>());
+  MOCK_METHOD1(GetDeviceFromInterfaceName,
+               scoped_refptr<Device>(const std::string& interface_name));
+  MOCK_METHOD1(ClaimInterface, void(const std::string& interface_name));
+  MOCK_METHOD1(ReleaseInterface, void(const std::string& interface_name));
+  MOCK_METHOD1(RequestDHCPPortAccess, void(const std::string& interface));
+  MOCK_METHOD1(ReleaseDHCPPortAccess, void(const std::string& interface));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockManager);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_MANAGER_H_
diff --git a/mock_process_factory.cc b/mock_process_factory.cc
new file mode 100644
index 0000000..587c7cd
--- /dev/null
+++ b/mock_process_factory.cc
@@ -0,0 +1,21 @@
+// Copyright 2015 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 "apmanager/mock_process_factory.h"
+
+namespace apmanager {
+
+namespace {
+base::LazyInstance<MockProcessFactory> g_mock_process_factory
+    = LAZY_INSTANCE_INITIALIZER;
+}  // namespace
+
+MockProcessFactory::MockProcessFactory() {}
+MockProcessFactory::~MockProcessFactory() {}
+
+MockProcessFactory* MockProcessFactory::GetInstance() {
+  return g_mock_process_factory.Pointer();
+}
+
+}  // namespace apmanager
diff --git a/mock_process_factory.h b/mock_process_factory.h
new file mode 100644
index 0000000..4a598ce
--- /dev/null
+++ b/mock_process_factory.h
@@ -0,0 +1,35 @@
+// Copyright 2015 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 APMANAGER_MOCK_PROCESS_FACTORY_H_
+#define APMANAGER_MOCK_PROCESS_FACTORY_H_
+
+#include <base/lazy_instance.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/process_factory.h"
+
+namespace apmanager {
+
+class MockProcessFactory : public ProcessFactory {
+ public:
+  ~MockProcessFactory() override;
+
+  // This is a singleton. Use MockDHCPServerFactory::GetInstance()->Foo().
+  static MockProcessFactory* GetInstance();
+
+  MOCK_METHOD0(CreateProcess, chromeos::Process*());
+
+ protected:
+  MockProcessFactory();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<MockProcessFactory>;
+
+  DISALLOW_COPY_AND_ASSIGN(MockProcessFactory);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_PROCESS_FACTORY_H_
diff --git a/mock_service.cc b/mock_service.cc
new file mode 100644
index 0000000..da17b13
--- /dev/null
+++ b/mock_service.cc
@@ -0,0 +1,13 @@
+// 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 "apmanager/mock_service.h"
+
+namespace apmanager {
+
+MockService::MockService() : Service(nullptr, 0) {}
+
+MockService::~MockService() {}
+
+}  // namespace apmanager
diff --git a/mock_service.h b/mock_service.h
new file mode 100644
index 0000000..13087d5
--- /dev/null
+++ b/mock_service.h
@@ -0,0 +1,29 @@
+// 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 APMANAGER_MOCK_SERVICE_H_
+#define APMANAGER_MOCK_SERVICE_H_
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+#include "apmanager/service.h"
+
+namespace apmanager {
+
+class MockService : public Service {
+ public:
+  MockService();
+  ~MockService() override;
+
+  MOCK_METHOD1(Start, bool(chromeos::ErrorPtr *error));
+  MOCK_METHOD1(Stop, bool(chromeos::ErrorPtr *error));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockService);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_MOCK_SERVICE_H_
diff --git a/process_factory.cc b/process_factory.cc
new file mode 100644
index 0000000..f7456a3
--- /dev/null
+++ b/process_factory.cc
@@ -0,0 +1,27 @@
+// Copyright 2015 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 "apmanager/process_factory.h"
+
+namespace apmanager {
+
+namespace {
+
+base::LazyInstance<ProcessFactory> g_process_factory
+    = LAZY_INSTANCE_INITIALIZER;
+
+}  // namespace
+
+ProcessFactory::ProcessFactory() {}
+ProcessFactory::~ProcessFactory() {}
+
+ProcessFactory* ProcessFactory::GetInstance() {
+  return g_process_factory.Pointer();
+}
+
+chromeos::Process* ProcessFactory::CreateProcess() {
+  return new chromeos::ProcessImpl();
+}
+
+}  // namespace apmanager
diff --git a/process_factory.h b/process_factory.h
new file mode 100644
index 0000000..967dc67
--- /dev/null
+++ b/process_factory.h
@@ -0,0 +1,36 @@
+// Copyright 2015 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 APMANAGER_PROCESS_FACTORY_H_
+#define APMANAGER_PROCESS_FACTORY_H_
+
+#include <string>
+
+#include <base/lazy_instance.h>
+
+#include <chromeos/process.h>
+
+namespace apmanager {
+
+class ProcessFactory {
+ public:
+  virtual ~ProcessFactory();
+
+  // This is a singleton. Use ProcessFactory::GetInstance()->Foo().
+  static ProcessFactory* GetInstance();
+
+  virtual chromeos::Process* CreateProcess();
+
+ protected:
+  ProcessFactory();
+
+ private:
+  friend struct base::DefaultLazyInstanceTraits<ProcessFactory>;
+
+  DISALLOW_COPY_AND_ASSIGN(ProcessFactory);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_PROCESS_FACTORY_H_
diff --git a/service.cc b/service.cc
new file mode 100644
index 0000000..b5a1f26
--- /dev/null
+++ b/service.cc
@@ -0,0 +1,224 @@
+// 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 "apmanager/service.h"
+
+#include <signal.h>
+
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <chromeos/errors/error.h>
+
+#include "apmanager/manager.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::ExportedObjectManager;
+using org::chromium::apmanager::ManagerAdaptor;
+using std::string;
+
+namespace apmanager {
+
+// static.
+const char Service::kHostapdPath[] = "/usr/sbin/hostapd";
+const char Service::kHostapdConfigPathFormat[] =
+    "/var/run/apmanager/hostapd/hostapd-%d.conf";
+const char Service::kHostapdControlInterfacePath[] =
+    "/var/run/apmanager/hostapd/ctrl_iface";
+const int Service::kTerminationTimeoutSeconds = 2;
+
+// static. Service state definitions.
+const char Service::kStateIdle[] = "Idle";
+const char Service::kStateStarting[] = "Starting";
+const char Service::kStateStarted[] = "Started";
+const char Service::kStateFailed[] = "Failed";
+
+Service::Service(Manager* manager, int service_identifier)
+    : org::chromium::apmanager::ServiceAdaptor(this),
+      manager_(manager),
+      identifier_(service_identifier),
+      service_path_(
+          base::StringPrintf("%s/services/%d",
+                             ManagerAdaptor::GetObjectPath().value().c_str(),
+                             service_identifier)),
+      dbus_path_(dbus::ObjectPath(service_path_)),
+      config_(new Config(manager, service_path_)),
+      dhcp_server_factory_(DHCPServerFactory::GetInstance()),
+      file_writer_(FileWriter::GetInstance()),
+      process_factory_(ProcessFactory::GetInstance()) {
+  SetConfig(config_->dbus_path());
+  SetState(kStateIdle);
+  // TODO(zqiu): come up with better server address management. This is good
+  // enough for now.
+  config_->SetServerAddressIndex(identifier_ & 0xFF);
+}
+
+Service::~Service() {
+  // Stop hostapd process if still running.
+  if (IsHostapdRunning()) {
+    ReleaseResources();
+  }
+}
+
+void Service::RegisterAsync(ExportedObjectManager* object_manager,
+                            const scoped_refptr<dbus::Bus>& bus,
+                            AsyncEventSequencer* sequencer) {
+  CHECK(!dbus_object_) << "Already registered";
+  dbus_object_.reset(
+      new chromeos::dbus_utils::DBusObject(
+          object_manager,
+          bus,
+          dbus_path_));
+  RegisterWithDBusObject(dbus_object_.get());
+  dbus_object_->RegisterAsync(
+      sequencer->GetHandler("Service.RegisterAsync() failed.", true));
+
+  // Register Config DBus object.
+  config_->RegisterAsync(object_manager, bus, sequencer);
+}
+
+bool Service::Start(chromeos::ErrorPtr* error) {
+  if (IsHostapdRunning()) {
+    chromeos::Error::AddTo(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
+        "Service already running");
+    return false;
+  }
+
+  // Setup hostapd control interface path.
+  config_->set_control_interface(kHostapdControlInterfacePath);
+
+  // Generate hostapd configuration content.
+  string config_str;
+  if (!config_->GenerateConfigFile(error, &config_str)) {
+    chromeos::Error::AddTo(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
+        "Failed to generate config file");
+    return false;
+  }
+
+  // Write configuration to a file.
+  string config_file_name = base::StringPrintf(kHostapdConfigPathFormat,
+                                               identifier_);
+  if (!file_writer_->Write(config_file_name, config_str)) {
+    chromeos::Error::AddTo(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
+        "Failed to write configuration to a file");
+    return false;
+  }
+
+  // Claim the device needed for this ap service.
+  if (!config_->ClaimDevice()) {
+    chromeos::Error::AddTo(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
+        "Failed to claim the device for this service");
+    return false;
+  }
+
+  // Start hostapd process.
+  if (!StartHostapdProcess(config_file_name)) {
+    chromeos::Error::AddTo(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
+        "Failed to start hostapd");
+    // Release the device claimed for this service.
+    config_->ReleaseDevice();
+    return false;
+  }
+
+  // Start DHCP server if in server mode.
+  if (config_->GetOperationMode() == kOperationModeServer) {
+    dhcp_server_.reset(
+        dhcp_server_factory_->CreateDHCPServer(config_->GetServerAddressIndex(),
+                                               config_->selected_interface()));
+    if (!dhcp_server_->Start()) {
+      chromeos::Error::AddTo(
+          error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
+          "Failed to start DHCP server");
+      ReleaseResources();
+      return false;
+    }
+    manager_->RequestDHCPPortAccess(config_->selected_interface());
+  }
+
+  // Start monitoring hostapd.
+  if (!hostapd_monitor_) {
+    hostapd_monitor_.reset(
+        new HostapdMonitor(base::Bind(&Service::HostapdEventCallback,
+                                      base::Unretained(this)),
+                           config_->control_interface(),
+                           config_->selected_interface()));
+  }
+  hostapd_monitor_->Start();
+
+  // Update service state.
+  SetState(kStateStarting);
+
+  return true;
+}
+
+bool Service::Stop(chromeos::ErrorPtr* error) {
+  if (!IsHostapdRunning()) {
+    chromeos::Error::AddTo(
+        error, FROM_HERE, chromeos::errors::dbus::kDomain, kServiceError,
+        "Service is not currently running");
+    return false;
+  }
+
+  ReleaseResources();
+  SetState(kStateIdle);
+  return true;
+}
+
+bool Service::IsHostapdRunning() {
+  return hostapd_process_ && hostapd_process_->pid() != 0 &&
+         chromeos::Process::ProcessExists(hostapd_process_->pid());
+}
+
+bool Service::StartHostapdProcess(const string& config_file_path) {
+  hostapd_process_.reset(process_factory_->CreateProcess());
+  hostapd_process_->AddArg(kHostapdPath);
+  hostapd_process_->AddArg(config_file_path);
+  if (!hostapd_process_->Start()) {
+    hostapd_process_.reset();
+    return false;
+  }
+  return true;
+}
+
+void Service::StopHostapdProcess() {
+  if (!hostapd_process_->Kill(SIGTERM, kTerminationTimeoutSeconds)) {
+    hostapd_process_->Kill(SIGKILL, kTerminationTimeoutSeconds);
+  }
+  hostapd_process_.reset();
+}
+
+void Service::ReleaseResources() {
+  hostapd_monitor_.reset();
+  StopHostapdProcess();
+  dhcp_server_.reset();
+  config_->ReleaseDevice();
+  manager_->ReleaseDHCPPortAccess(config_->selected_interface());
+}
+
+void Service::HostapdEventCallback(HostapdMonitor::Event event,
+                                   const std::string& data) {
+  switch (event) {
+    case HostapdMonitor::kHostapdFailed:
+      SetState(kStateFailed);
+      break;
+    case HostapdMonitor::kHostapdStarted:
+      SetState(kStateStarted);
+      break;
+    case HostapdMonitor::kStationConnected:
+      LOG(INFO) << "Station connected: " << data;
+      break;
+    case HostapdMonitor::kStationDisconnected:
+      LOG(INFO) << "Station disconnected: " << data;
+      break;
+    default:
+      LOG(ERROR) << "Unknown event: " << event;
+      break;
+  }
+}
+
+}  // namespace apmanager
diff --git a/service.h b/service.h
new file mode 100644
index 0000000..edd3b24
--- /dev/null
+++ b/service.h
@@ -0,0 +1,91 @@
+// 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 APMANAGER_SERVICE_H_
+#define APMANAGER_SERVICE_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <chromeos/process.h>
+
+#include "apmanager/config.h"
+#include "apmanager/dbus_adaptors/org.chromium.apmanager.Service.h"
+#include "apmanager/dhcp_server_factory.h"
+#include "apmanager/file_writer.h"
+#include "apmanager/hostapd_monitor.h"
+#include "apmanager/process_factory.h"
+
+namespace apmanager {
+
+class Manager;
+
+class Service : public org::chromium::apmanager::ServiceAdaptor,
+                public org::chromium::apmanager::ServiceInterface {
+ public:
+  Service(Manager* manager, int service_identifier);
+  virtual ~Service();
+
+  // Implementation of ServiceInterface.
+  virtual bool Start(chromeos::ErrorPtr* error);
+  virtual bool Stop(chromeos::ErrorPtr* error);
+
+  // Register Service DBus object.
+  void RegisterAsync(
+      chromeos::dbus_utils::ExportedObjectManager* object_manager,
+      const scoped_refptr<dbus::Bus>& bus,
+      chromeos::dbus_utils::AsyncEventSequencer* sequencer);
+
+  const dbus::ObjectPath& dbus_path() const { return dbus_path_; }
+
+  int identifier() const { return identifier_; }
+
+ private:
+  friend class ServiceTest;
+
+  static const char kHostapdPath[];
+  static const char kHostapdConfigPathFormat[];
+  static const char kHostapdControlInterfacePath[];
+  static const int kTerminationTimeoutSeconds;
+  static const char kStateIdle[];
+  static const char kStateStarting[];
+  static const char kStateStarted[];
+  static const char kStateFailed[];
+
+  // Return true if hostapd process is currently running.
+  bool IsHostapdRunning();
+
+  // Start hostapd process. Return true if process is created/started
+  // successfully, false otherwise.
+  bool StartHostapdProcess(const std::string& config_file_path);
+
+  // Stop the running hostapd process. Sending it a SIGTERM signal first, then
+  // a SIGKILL if failed to terminated with SIGTERM.
+  void StopHostapdProcess();
+
+  // Release resources allocated to this service.
+  void ReleaseResources();
+
+  void HostapdEventCallback(HostapdMonitor::Event event,
+                            const std::string& data);
+
+  Manager* manager_;
+  int identifier_;
+  std::string service_path_;
+  dbus::ObjectPath dbus_path_;
+  std::unique_ptr<Config> config_;
+  std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+  std::unique_ptr<chromeos::Process> hostapd_process_;
+  std::unique_ptr<DHCPServer> dhcp_server_;
+  DHCPServerFactory* dhcp_server_factory_;
+  FileWriter* file_writer_;
+  ProcessFactory* process_factory_;
+  std::unique_ptr<HostapdMonitor> hostapd_monitor_;
+
+  DISALLOW_COPY_AND_ASSIGN(Service);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_SERVICE_H_
diff --git a/service_unittest.cc b/service_unittest.cc
new file mode 100644
index 0000000..5170824
--- /dev/null
+++ b/service_unittest.cc
@@ -0,0 +1,147 @@
+// 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 "apmanager/service.h"
+
+#include <string>
+
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+#include <chromeos/process_mock.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "apmanager/mock_config.h"
+#include "apmanager/mock_dhcp_server.h"
+#include "apmanager/mock_dhcp_server_factory.h"
+#include "apmanager/mock_file_writer.h"
+#include "apmanager/mock_hostapd_monitor.h"
+#include "apmanager/mock_manager.h"
+#include "apmanager/mock_process_factory.h"
+
+using chromeos::ProcessMock;
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+namespace {
+  const int kServiceIdentifier = 1;
+  const char kHostapdConfig[] = "ssid=test\n";
+  const char kBinSleep[] = "/bin/sleep";
+  const char kHostapdConfigFilePath[] =
+      "/var/run/apmanager/hostapd/hostapd-1.conf";
+}  // namespace
+
+namespace apmanager {
+
+class ServiceTest : public testing::Test {
+ public:
+  ServiceTest()
+      : dhcp_server_factory_(MockDHCPServerFactory::GetInstance()),
+        file_writer_(MockFileWriter::GetInstance()),
+        process_factory_(MockProcessFactory::GetInstance()),
+        hostapd_monitor_(new MockHostapdMonitor()),
+        service_(&manager_, kServiceIdentifier) {}
+
+  virtual void SetUp() {
+    service_.dhcp_server_factory_ = dhcp_server_factory_;
+    service_.file_writer_ = file_writer_;
+    service_.process_factory_ = process_factory_;
+    service_.hostapd_monitor_.reset(hostapd_monitor_);
+  }
+
+  void StartDummyProcess() {
+    service_.hostapd_process_.reset(new chromeos::ProcessImpl);
+    service_.hostapd_process_->AddArg(kBinSleep);
+    service_.hostapd_process_->AddArg("12345");
+    CHECK(service_.hostapd_process_->Start());
+    LOG(INFO) << "DummyProcess: " << service_.hostapd_process_->pid();
+  }
+
+  void SetConfig(Config* config) {
+    service_.config_.reset(config);
+  }
+
+ protected:
+  MockManager manager_;
+  MockDHCPServerFactory* dhcp_server_factory_;
+  MockFileWriter* file_writer_;
+  MockProcessFactory* process_factory_;
+  MockHostapdMonitor* hostapd_monitor_;
+  Service service_;
+};
+
+MATCHER_P(IsServiceErrorStartingWith, message, "") {
+  return arg != nullptr &&
+         arg->GetDomain() == chromeos::errors::dbus::kDomain &&
+         arg->GetCode() == kServiceError &&
+         base::EndsWith(arg->GetMessage(), message, false);
+}
+
+TEST_F(ServiceTest, StartWhenServiceAlreadyRunning) {
+  StartDummyProcess();
+
+  chromeos::ErrorPtr error;
+  EXPECT_FALSE(service_.Start(&error));
+  EXPECT_THAT(error, IsServiceErrorStartingWith("Service already running"));
+}
+
+TEST_F(ServiceTest, StartWhenConfigFileFailed) {
+  MockConfig* config = new MockConfig();
+  SetConfig(config);
+
+  chromeos::ErrorPtr error;
+  EXPECT_CALL(*config, GenerateConfigFile(_, _)).WillOnce(Return(false));
+  EXPECT_FALSE(service_.Start(&error));
+  EXPECT_THAT(error, IsServiceErrorStartingWith(
+      "Failed to generate config file"));
+}
+
+TEST_F(ServiceTest, StartSuccess) {
+  MockConfig* config = new MockConfig();
+  SetConfig(config);
+
+  // Setup mock DHCP server.
+  MockDHCPServer* dhcp_server = new MockDHCPServer();
+  // Setup mock process.
+  ProcessMock* process = new ProcessMock();
+
+  std::string config_str(kHostapdConfig);
+  chromeos::ErrorPtr error;
+  EXPECT_CALL(*config, GenerateConfigFile(_, _)).WillOnce(
+      DoAll(SetArgPointee<1>(config_str), Return(true)));
+  EXPECT_CALL(*file_writer_, Write(kHostapdConfigFilePath, kHostapdConfig))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*config, ClaimDevice()).WillOnce(Return(true));
+  EXPECT_CALL(*process_factory_, CreateProcess()).WillOnce(Return(process));
+  EXPECT_CALL(*process, Start()).WillOnce(Return(true));
+  EXPECT_CALL(*dhcp_server_factory_, CreateDHCPServer(_, _))
+      .WillOnce(Return(dhcp_server));
+  EXPECT_CALL(*dhcp_server, Start()).WillOnce(Return(true));
+  EXPECT_CALL(manager_, RequestDHCPPortAccess(_));
+  EXPECT_CALL(*hostapd_monitor_, Start());
+  EXPECT_TRUE(service_.Start(&error));
+  EXPECT_EQ(nullptr, error);
+}
+
+TEST_F(ServiceTest, StopWhenServiceNotRunning) {
+  chromeos::ErrorPtr error;
+  EXPECT_FALSE(service_.Stop(&error));
+  EXPECT_THAT(error, IsServiceErrorStartingWith(
+      "Service is not currently running"));
+}
+
+TEST_F(ServiceTest, StopSuccess) {
+  StartDummyProcess();
+
+  MockConfig* config = new MockConfig();
+  SetConfig(config);
+  chromeos::ErrorPtr error;
+  EXPECT_CALL(*config, ReleaseDevice()).Times(1);
+  EXPECT_TRUE(service_.Stop(&error));
+}
+
+}  // namespace apmanager
diff --git a/shill_proxy.cc b/shill_proxy.cc
new file mode 100644
index 0000000..3a6dd4d
--- /dev/null
+++ b/shill_proxy.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 "apmanager/shill_proxy.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/service_constants.h>
+#include <chromeos/errors/error.h>
+
+using std::string;
+
+namespace apmanager {
+
+// static.
+const char ShillProxy::kManagerPath[] = "/";
+
+ShillProxy::ShillProxy() {}
+
+ShillProxy::~ShillProxy() {}
+
+void ShillProxy::Init(const scoped_refptr<dbus::Bus>& bus) {
+  CHECK(!manager_proxy_) << "Already init";
+  manager_proxy_.reset(
+      new org::chromium::flimflam::ManagerProxy(
+          bus, shill::kFlimflamServiceName, dbus::ObjectPath(kManagerPath)));
+  // This will connect the name owner changed signal in DBus object proxy,
+  // The callback will be invoked as soon as service is avalilable. and will
+  // be cleared after it is invoked. So this will be an one time callback.
+  manager_proxy_->GetObjectProxy()->WaitForServiceToBeAvailable(
+      base::Bind(&ShillProxy::OnServiceAvailable, base::Unretained(this)));
+  // This will continuously monitor the name owner of the service. However,
+  // it does not connect the name owner changed signal in DBus object proxy
+  // for some reason. In order to connect the name owner changed signal,
+  // either WaitForServiceToBeAvaiable or ConnectToSignal need to be invoked.
+  // Since we're not interested in any signals from Shill proxy,
+  // WaitForServiceToBeAvailable is used.
+  manager_proxy_->GetObjectProxy()->SetNameOwnerChangedCallback(
+      base::Bind(&ShillProxy::OnServiceNameChanged, base::Unretained(this)));
+}
+
+void ShillProxy::ClaimInterface(const string& interface_name) {
+  CHECK(manager_proxy_) << "Proxy not initialize yet";
+  chromeos::ErrorPtr error;
+  if (!manager_proxy_->ClaimInterface(kServiceName, interface_name, &error)) {
+    // Ignore unknown object error (when shill is not running). Only report
+    // internal error from shill.
+    if (error->GetCode() != DBUS_ERROR_UNKNOWN_OBJECT) {
+      LOG(ERROR) << "Failed to claim interface from shill: "
+                 << error->GetCode() << " " << error->GetMessage();
+    }
+  }
+  claimed_interfaces_.insert(interface_name);
+}
+
+void ShillProxy::ReleaseInterface(const string& interface_name) {
+  CHECK(manager_proxy_) << "Proxy not initialize yet";
+  chromeos::ErrorPtr error;
+  if (!manager_proxy_->ReleaseInterface(kServiceName, interface_name, &error)) {
+    // Ignore unknown object error (when shill is not running). Only report
+    // internal error from shill.
+    if (error->GetCode() != DBUS_ERROR_UNKNOWN_OBJECT) {
+      LOG(ERROR) << "Failed to release interface from shill: "
+                 << error->GetCode() << " " << error->GetMessage();
+    }
+  }
+  claimed_interfaces_.erase(interface_name);
+}
+
+void ShillProxy::OnServiceAvailable(bool service_available) {
+  LOG(INFO) << "OnServiceAvailabe " << service_available;
+  // Nothing to be done if proxy service not available.
+  if (!service_available) {
+    return;
+  }
+  // Claim all interfaces from shill DBus service in case this is a new
+  // instance.
+  for (const auto& interface : claimed_interfaces_) {
+    chromeos::ErrorPtr error;
+    if (!manager_proxy_->ClaimInterface(kServiceName, interface, &error)) {
+      LOG(ERROR) << "Failed to claim interface from shill: "
+                 << error->GetCode() << " " << error->GetMessage();
+    }
+  }
+}
+
+void ShillProxy::OnServiceNameChanged(const string& old_owner,
+                                         const string& new_owner) {
+  LOG(INFO) << "OnServiceNameChanged old " << old_owner
+            << " new " << new_owner;
+  // Nothing to be done if no owner is attached to the shill service.
+  if (new_owner.empty()) {
+    return;
+  }
+  OnServiceAvailable(true);
+}
+
+}  // namespace apmanager
diff --git a/shill_proxy.h b/shill_proxy.h
new file mode 100644
index 0000000..24a0508
--- /dev/null
+++ b/shill_proxy.h
@@ -0,0 +1,48 @@
+// 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 APMANAGER_SHILL_PROXY_H_
+#define APMANAGER_SHILL_PROXY_H_
+
+#include <set>
+#include <string>
+
+#include <base/macros.h>
+#include <base/memory/scoped_ptr.h>
+
+#include "shill/dbus-proxies.h"
+
+// Proxy for shill "org.chromium.flimflam" DBus service.
+namespace apmanager {
+
+class ShillProxy {
+ public:
+  ShillProxy();
+  virtual ~ShillProxy();
+
+  void Init(const scoped_refptr<dbus::Bus>& bus);
+
+  // Claim the given interface |interface_name| from shill.
+  virtual void ClaimInterface(const std::string& interface_name);
+  // Release the given interface |interface_name| to shill.
+  virtual void ReleaseInterface(const std::string& interface_name);
+
+ private:
+  void OnServiceAvailable(bool service_available);
+  void OnServiceNameChanged(const std::string& old_owner,
+                            const std::string& new_owner);
+
+  static const char kManagerPath[];
+
+  // DBus proxy for shill manager.
+  std::unique_ptr<org::chromium::flimflam::ManagerProxy> manager_proxy_;
+  // List of interfaces apmanager have claimed.
+  std::set<std::string> claimed_interfaces_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShillProxy);
+};
+
+}  // namespace apmanager
+
+#endif  // APMANAGER_SHILL_PROXY_H_
diff --git a/testrunner.cc b/testrunner.cc
new file mode 100644
index 0000000..51bace0
--- /dev/null
+++ b/testrunner.cc
@@ -0,0 +1,16 @@
+// 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/at_exit.h>
+#include <base/command_line.h>
+#include <chromeos/syslog_logging.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+  base::AtExitManager exit_manager;
+  base::CommandLine::Init(argc, argv);
+  chromeos::InitLog(chromeos::kLogToStderr);
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}