Move supplicant management functions to delegate

This makes them easier to mock out in unit testing.

Bug: 29620309
Test: wificond integration tests continue to pass

Change-Id: I5b794b9b5c7fb291d253e28ca5f60cd69f39c280
diff --git a/libwifi_system/Android.mk b/libwifi_system/Android.mk
index 61d340a..f969247 100644
--- a/libwifi_system/Android.mk
+++ b/libwifi_system/Android.mk
@@ -54,6 +54,7 @@
     hostapd_manager.cpp \
     interface_tool.cpp \
     hal_tool.cpp \
+    supplicant_manager.cpp \
     wifi.cpp
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/libwifi_system/include/wifi_system/supplicant_manager.h b/libwifi_system/include/wifi_system/supplicant_manager.h
new file mode 100644
index 0000000..c666fc9
--- /dev/null
+++ b/libwifi_system/include/wifi_system/supplicant_manager.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_SUPPLICANT_MANAGER_H
+#define ANDROID_WIFI_SYSTEM_SUPPLICANT_MANAGER_H
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace wifi_system {
+
+class SupplicantManager {
+ public:
+  SupplicantManager() = default;
+  virtual ~SupplicantManager() = default;
+
+  // Request that supplicant be started.
+  // Returns true on success.
+  virtual bool StartSupplicant();
+
+  // Request that a running instance of supplicant be stopped.
+  // Returns true on success.
+  virtual bool StopSupplicant();
+
+  // Returns true iff supplicant is still running.
+  virtual bool IsSupplicantRunning();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SupplicantManager);
+};  // class SupplicantManager
+
+}  // namespace wifi_system
+}  // namespace android
+
+#endif  // ANDROID_WIFI_SYSTEM_SUPPLICANT_MANAGER_H
diff --git a/libwifi_system/include/wifi_system/wifi.h b/libwifi_system/include/wifi_system/wifi.h
index 0cfbf3e..6d017ec 100644
--- a/libwifi_system/include/wifi_system/wifi.h
+++ b/libwifi_system/include/wifi_system/wifi.h
@@ -25,20 +25,6 @@
 extern const char kWiFiEntropyFile[];
 
 /**
- * Start supplicant.
- *
- * @return 0 on success, < 0 on failure.
- */
-int wifi_start_supplicant();
-
-/**
- * Stop supplicant.
- *
- * @return 0 on success, < 0 on failure.
- */
-int wifi_stop_supplicant();
-
-/**
  * Open a connection to supplicant
  *
  * @return 0 on success, < 0 on failure.
diff --git a/libwifi_system/supplicant_manager.cpp b/libwifi_system/supplicant_manager.cpp
new file mode 100644
index 0000000..833739a
--- /dev/null
+++ b/libwifi_system/supplicant_manager.cpp
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wifi_system/supplicant_manager.h"
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+// This ugliness is necessary to access internal implementation details
+// of the property subsystem.
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+// Certain system (emulators) don't have wpa_supplicant installed at all.
+#ifdef LIBWPA_CLIENT_EXISTS
+#include <libwpa_client/wpa_ctrl.h>
+#else
+void wpa_ctrl_cleanup(void) {}
+#endif
+
+#include "wifi_system/wifi.h"
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant";
+const char kSupplicantConfigTemplatePath[] =
+    "/system/etc/wifi/wpa_supplicant.conf";
+const char kSupplicantConfigFile[] = "/data/misc/wifi/wpa_supplicant.conf";
+const char kP2pConfigFile[] = "/data/misc/wifi/p2p_supplicant.conf";
+const char kSupplicantServiceName[] = "wpa_supplicant";
+constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+int ensure_config_file_exists(const char* config_file) {
+  char buf[2048];
+  int srcfd, destfd;
+  int nread;
+  int ret;
+
+  ret = access(config_file, R_OK | W_OK);
+  if ((ret == 0) || (errno == EACCES)) {
+    if ((ret != 0) && (chmod(config_file, kConfigFileMode) != 0)) {
+      LOG(ERROR) << "Cannot set RW to \"" << config_file << "\": "
+                 << strerror(errno);
+      return false;
+    }
+    return true;
+  } else if (errno != ENOENT) {
+    LOG(ERROR) << "Cannot access \"" << config_file << "\": "
+               << strerror(errno);
+    return false;
+  }
+
+  srcfd = TEMP_FAILURE_RETRY(open(kSupplicantConfigTemplatePath, O_RDONLY));
+  if (srcfd < 0) {
+    LOG(ERROR) << "Cannot open \"" << kSupplicantConfigTemplatePath << "\": "
+               << strerror(errno);
+    return false;
+  }
+
+  destfd = TEMP_FAILURE_RETRY(open(config_file,
+                                   O_CREAT | O_RDWR,
+                                   kConfigFileMode));
+  if (destfd < 0) {
+    close(srcfd);
+    LOG(ERROR) << "Cannot create \"" << config_file << "\": "
+               << strerror(errno);
+    return false;
+  }
+
+  while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
+    if (nread < 0) {
+      LOG(ERROR) << "Error reading \"" << kSupplicantConfigTemplatePath
+                 << "\": " << strerror(errno);
+      close(srcfd);
+      close(destfd);
+      unlink(config_file);
+      return false;
+    }
+    TEMP_FAILURE_RETRY(write(destfd, buf, nread));
+  }
+
+  close(destfd);
+  close(srcfd);
+
+  /* chmod is needed because open() didn't set permisions properly */
+  if (chmod(config_file, kConfigFileMode) < 0) {
+    LOG(ERROR) << "Error changing permissions of " << config_file
+               << " to 0660: " << strerror(errno);
+    unlink(config_file);
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace
+
+bool SupplicantManager::StartSupplicant() {
+  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+  int count = 200; /* wait at most 20 seconds for completion */
+  const prop_info* pi;
+  unsigned serial = 0;
+
+  /* Check whether already running */
+  if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
+      strcmp(supp_status, "running") == 0) {
+    return true;
+  }
+
+  /* Before starting the daemon, make sure its config file exists */
+  if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
+    LOG(ERROR) << "Wi-Fi will not be enabled";
+    return false;
+  }
+
+  /*
+   * Some devices have another configuration file for the p2p interface.
+   * However, not all devices have this, and we'll let it slide if it
+   * is missing.  For devices that do expect this file to exist,
+   * supplicant will refuse to start and emit a good error message.
+   * No need to check for it here.
+   */
+  (void)ensure_config_file_exists(kP2pConfigFile);
+
+  if (ensure_entropy_file_exists() < 0) {
+    LOG(ERROR) << "Wi-Fi entropy file was not created";
+  }
+
+  /* Clear out any stale socket files that might be left over. */
+  wpa_ctrl_cleanup();
+
+  /*
+   * Get a reference to the status property, so we can distinguish
+   * the case where it goes stopped => running => stopped (i.e.,
+   * it start up, but fails right away) from the case in which
+   * it starts in the stopped state and never manages to start
+   * running at all.
+   */
+  pi = __system_property_find(kSupplicantInitProperty);
+  if (pi != NULL) {
+    serial = __system_property_serial(pi);
+  }
+
+  property_set("ctl.start", kSupplicantServiceName);
+  sched_yield();
+
+  while (count-- > 0) {
+    if (pi == NULL) {
+      pi = __system_property_find(kSupplicantInitProperty);
+    }
+    if (pi != NULL) {
+      /*
+       * property serial updated means that init process is scheduled
+       * after we sched_yield, further property status checking is based on this
+       */
+      if (__system_property_serial(pi) != serial) {
+        __system_property_read(pi, NULL, supp_status);
+        if (strcmp(supp_status, "running") == 0) {
+          return true;
+        } else if (strcmp(supp_status, "stopped") == 0) {
+          return false;
+        }
+      }
+    }
+    usleep(100000);
+  }
+  return false;
+}
+
+bool SupplicantManager::StopSupplicant() {
+  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+  int count = 50; /* wait at most 5 seconds for completion */
+
+  /* Check whether supplicant already stopped */
+  if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
+      strcmp(supp_status, "stopped") == 0) {
+    return true;
+  }
+
+  property_set("ctl.stop", kSupplicantServiceName);
+  sched_yield();
+
+  while (count-- > 0) {
+    if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
+      if (strcmp(supp_status, "stopped") == 0) return true;
+    }
+    usleep(100000);
+  }
+  LOG(ERROR) << "Failed to stop supplicant";
+  return false;
+}
+
+bool SupplicantManager::IsSupplicantRunning() {
+  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+  if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
+    return strcmp(supp_status, "running") == 0;
+  }
+  return false;  // Failed to read service status from init.
+}
+
+}  // namespace wifi_system
+}  // namespace android
diff --git a/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h b/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h
new file mode 100644
index 0000000..01d604f
--- /dev/null
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_WIFI_SYSTEM_TEST_MOCK_SUPPLICANT_MANAGER_H
+#define ANDROID_WIFI_SYSTEM_TEST_MOCK_SUPPLICANT_MANAGER_H
+
+#include <wifi_system/supplicant_manager.h>
+
+namespace android {
+namespace wifi_system {
+
+class MockSupplicantManager : public SupplicantManager {
+ public:
+  ~MockSupplicantManager() override = default;
+
+  MOCK_METHOD0(StartSupplicant, bool());
+  MOCK_METHOD0(StopSupplicant, bool());
+  MOCK_METHOD0(IsSupplicantRunning, bool());
+
+};  // class MockSupplicantManager
+
+}  // namespace wifi_system
+}  // namespace android
+
+#endif  // ANDROID_WIFI_SYSTEM_TEST_MOCK_SUPPLICANT_MANAGER_H
diff --git a/libwifi_system/wifi.cpp b/libwifi_system/wifi.cpp
index 49aee18..e76a938 100644
--- a/libwifi_system/wifi.cpp
+++ b/libwifi_system/wifi.cpp
@@ -36,6 +36,8 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
+#include "wifi_system/supplicant_manager.h"
+
 #ifdef LIBWPA_CLIENT_EXISTS
 #include <libwpa_client/wpa_ctrl.h>
 #else
@@ -77,11 +79,6 @@
 #define WIFI_DRIVER_LOADER_DELAY 1000000
 
 const char IFACE_DIR[] = "/data/system/wpa_supplicant";
-const char SUPPLICANT_SERVICE_NAME[] = "wpa_supplicant";
-const char SUPPLICANT_INIT_PROPERTY[] = "init.svc.wpa_supplicant";
-const char SUPP_CONFIG_TEMPLATE[] = "/system/etc/wifi/wpa_supplicant.conf";
-const char SUPP_CONFIG_FILE[] = "/data/misc/wifi/wpa_supplicant.conf";
-const char P2P_CONFIG_FILE[] = "/data/misc/wifi/p2p_supplicant.conf";
 
 const char IFNAME[] = "IFNAME=";
 #define IFNAMELEN (sizeof(IFNAME) - 1)
@@ -113,170 +110,16 @@
   }
 }
 
-int ensure_config_file_exists(const char* config_file) {
-  char buf[2048];
-  int srcfd, destfd;
-  int nread;
-  int ret;
-
-  ret = access(config_file, R_OK | W_OK);
-  if ((ret == 0) || (errno == EACCES)) {
-    if ((ret != 0) &&
-        (chmod(config_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0)) {
-      ALOGE("Cannot set RW to \"%s\": %s", config_file, strerror(errno));
-      return -1;
-    }
-    return 0;
-  } else if (errno != ENOENT) {
-    ALOGE("Cannot access \"%s\": %s", config_file, strerror(errno));
-    return -1;
-  }
-
-  srcfd = TEMP_FAILURE_RETRY(open(SUPP_CONFIG_TEMPLATE, O_RDONLY));
-  if (srcfd < 0) {
-    ALOGE("Cannot open \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
-    return -1;
-  }
-
-  destfd = TEMP_FAILURE_RETRY(open(config_file, O_CREAT | O_RDWR, 0660));
-  if (destfd < 0) {
-    close(srcfd);
-    ALOGE("Cannot create \"%s\": %s", config_file, strerror(errno));
-    return -1;
-  }
-
-  while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
-    if (nread < 0) {
-      ALOGE("Error reading \"%s\": %s", SUPP_CONFIG_TEMPLATE, strerror(errno));
-      close(srcfd);
-      close(destfd);
-      unlink(config_file);
-      return -1;
-    }
-    TEMP_FAILURE_RETRY(write(destfd, buf, nread));
-  }
-
-  close(destfd);
-  close(srcfd);
-
-  /* chmod is needed because open() didn't set permisions properly */
-  if (chmod(config_file, 0660) < 0) {
-    ALOGE("Error changing permissions of %s to 0660: %s", config_file,
-          strerror(errno));
-    unlink(config_file);
-    return -1;
-  }
-
-  return 0;
-}
-
 }  // namespace
 
 const char kWiFiEntropyFile[] = "/data/misc/wifi/entropy.bin";
 
-int wifi_start_supplicant() {
-  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
-  int count = 200; /* wait at most 20 seconds for completion */
-  const prop_info* pi;
-  unsigned serial = 0;
-
-  /* Check whether already running */
-  if (property_get(SUPPLICANT_INIT_PROPERTY, supp_status, NULL) &&
-      strcmp(supp_status, "running") == 0) {
-    return 0;
-  }
-
-  /* Before starting the daemon, make sure its config file exists */
-  if (ensure_config_file_exists(SUPP_CONFIG_FILE) < 0) {
-    ALOGE("Wi-Fi will not be enabled");
-    return -1;
-  }
-
-  /*
-   * Some devices have another configuration file for the p2p interface.
-   * However, not all devices have this, and we'll let it slide if it
-   * is missing.  For devices that do expect this file to exist,
-   * supplicant will refuse to start and emit a good error message.
-   * No need to check for it here.
-   */
-  (void)ensure_config_file_exists(P2P_CONFIG_FILE);
-
-  if (ensure_entropy_file_exists() < 0) {
-    ALOGE("Wi-Fi entropy file was not created");
-  }
-
-  /* Clear out any stale socket files that might be left over. */
-  wpa_ctrl_cleanup();
-
-  /*
-   * Get a reference to the status property, so we can distinguish
-   * the case where it goes stopped => running => stopped (i.e.,
-   * it start up, but fails right away) from the case in which
-   * it starts in the stopped state and never manages to start
-   * running at all.
-   */
-  pi = __system_property_find(SUPPLICANT_INIT_PROPERTY);
-  if (pi != NULL) {
-    serial = __system_property_serial(pi);
-  }
-
-  property_set("ctl.start", SUPPLICANT_SERVICE_NAME);
-  sched_yield();
-
-  while (count-- > 0) {
-    if (pi == NULL) {
-      pi = __system_property_find(SUPPLICANT_INIT_PROPERTY);
-    }
-    if (pi != NULL) {
-      /*
-       * property serial updated means that init process is scheduled
-       * after we sched_yield, further property status checking is based on this
-       */
-      if (__system_property_serial(pi) != serial) {
-        __system_property_read(pi, NULL, supp_status);
-        if (strcmp(supp_status, "running") == 0) {
-          return 0;
-        } else if (strcmp(supp_status, "stopped") == 0) {
-          return -1;
-        }
-      }
-    }
-    usleep(100000);
-  }
-  return -1;
-}
-
-int wifi_stop_supplicant() {
-  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
-  int count = 50; /* wait at most 5 seconds for completion */
-
-  /* Check whether supplicant already stopped */
-  if (property_get(SUPPLICANT_INIT_PROPERTY, supp_status, NULL) &&
-      strcmp(supp_status, "stopped") == 0) {
-    return 0;
-  }
-
-  property_set("ctl.stop", SUPPLICANT_SERVICE_NAME);
-  sched_yield();
-
-  while (count-- > 0) {
-    if (property_get(SUPPLICANT_INIT_PROPERTY, supp_status, NULL)) {
-      if (strcmp(supp_status, "stopped") == 0) return 0;
-    }
-    usleep(100000);
-  }
-  ALOGE("Failed to stop supplicant");
-  return -1;
-}
-
 namespace {
 
 int wifi_connect_on_socket_path(const char* path) {
-  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
-
   /* Make sure supplicant is running */
-  if (!property_get(SUPPLICANT_INIT_PROPERTY, supp_status, NULL) ||
-      strcmp(supp_status, "running") != 0) {
+  android::wifi_system::SupplicantManager manager;
+  if (!manager.IsSupplicantRunning()) {
     ALOGE("Supplicant not running, cannot connect");
     return -1;
   }
@@ -331,20 +174,11 @@
   return 0;
 }
 
-int wifi_supplicant_connection_active() {
-  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
-
-  if (property_get(SUPPLICANT_INIT_PROPERTY, supp_status, NULL)) {
-    if (strcmp(supp_status, "stopped") == 0) return -1;
-  }
-
-  return 0;
-}
-
 int wifi_ctrl_recv(char* reply, size_t* reply_len) {
   int res;
   int ctrlfd = wpa_ctrl_get_fd(monitor_conn);
   struct pollfd rfds[2];
+  android::wifi_system::SupplicantManager manager;
 
   memset(rfds, 0, 2 * sizeof(struct pollfd));
   rfds[0].fd = ctrlfd;
@@ -360,8 +194,9 @@
       /* timed out, check if supplicant is active
        * or not ..
        */
-      res = wifi_supplicant_connection_active();
-      if (res < 0) return -2;
+      if (!manager.IsSupplicantRunning()) {
+        return -2;
+      }
     }
   } while (res == 0);
 
@@ -467,15 +302,15 @@
 }
 
 void wifi_close_supplicant_connection() {
-  char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
   int count =
       50; /* wait at most 5 seconds to ensure init has stopped stupplicant */
 
   wifi_close_sockets();
 
+  android::wifi_system::SupplicantManager manager;
   while (count-- > 0) {
-    if (property_get(SUPPLICANT_INIT_PROPERTY, supp_status, NULL)) {
-      if (strcmp(supp_status, "stopped") == 0) return;
+    if (!manager.IsSupplicantRunning()) {
+      return;
     }
     usleep(100000);
   }