apmanager: parse ap mode support information

Parse AP mode support information from Wiphy capability message. And do
not set preferred AP mode interface if AP mode is not supported.

BUG=chromium:431763
TEST=USE="asan clang" FEATURES=test emerge-$BOARD apmanager
Manual Test:
1. Verify AP service can be started on a device that supports AP mode.
2. Verify AP service failed to start on a device that doesn't
   support AP mode with a "No device available" error message.

Change-Id: Ia77fa3d74b944b978697afcb16aa71f27412fbdb
Reviewed-on: https://chromium-review.googlesource.com/233773
Reviewed-by: Peter Qiu <zqiu@chromium.org>
Commit-Queue: Peter Qiu <zqiu@chromium.org>
Trybot-Ready: Peter Qiu <zqiu@chromium.org>
Tested-by: Peter Qiu <zqiu@chromium.org>
diff --git a/device.cc b/device.cc
index e6489e4..1fbda40 100644
--- a/device.cc
+++ b/device.cc
@@ -22,7 +22,8 @@
 
 Device::Device(Manager* manager, const string& device_name)
     : org::chromium::apmanager::DeviceAdaptor(this),
-      manager_(manager) {
+      manager_(manager),
+      supports_ap_mode_(false) {
   SetDeviceName(device_name);
   SetInUsed(false);
 }
@@ -76,8 +77,15 @@
 }
 
 void Device::ParseWiphyCapability(const shill::Nl80211Message& msg) {
-  // TODO(zqiu): Parse NL80211_ATTR_SUPPORTED_IFTYPES for supported interface
-  // modes.
+  // 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;
@@ -339,7 +347,10 @@
 }
 
 void Device::UpdatePreferredAPInterface() {
-  // TODO(zqiu): return if device doesn't support AP interface mode.
+  // 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
diff --git a/device.h b/device.h
index 9b5e000..20240ab 100644
--- a/device.h
+++ b/device.h
@@ -111,6 +111,9 @@
   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_;
 
diff --git a/device_unittest.cc b/device_unittest.cc
index ebc38a7..997d94f 100644
--- a/device_unittest.cc
+++ b/device_unittest.cc
@@ -96,6 +96,14 @@
     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);
   }
@@ -137,6 +145,8 @@
 }
 
 TEST_F(DeviceTest, PreferredAPInterface) {
+  EnableApModeSupport();
+
   // Register a monitor mode interface, no preferred AP mode interface.
   device_->RegisterInterface(kMonitorModeInterface);
   VerifyPreferredApInterface("");
@@ -177,8 +187,30 @@
   VerifyPreferredApInterface("");
 }
 
-TEST_F(DeviceTest, ParseHTCapability) {
+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;
@@ -219,6 +251,9 @@
 
   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);
@@ -238,6 +273,8 @@
 }
 
 TEST_F(DeviceTest, ClaimAndReleaseDevice) {
+  EnableApModeSupport();
+
   // Register multiple interfaces.
   device_->RegisterInterface(kApModeInterface1);
   device_->RegisterInterface(kManagedModeInterface1);