text: /*
   * hidl interface for wpa_supplicant daemon
   * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
   * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
   *
   * This software may be distributed under the terms of the BSD license.
   * See README for more details.
   */
  
  #include "hidl_manager.h"
  #include "hidl_return_util.h"
  #include "supplicant.h"
  
  #include <android-base/file.h>
  #include <fcntl.h>
  #include <sys/stat.h>
  
  namespace {
  using namespace android::hardware::wifi::supplicant::V1_2;
  
  // Pre-populated interface params for interfaces controlled by wpa_supplicant.
  // Note: This may differ for other OEM's. So, modify this accordingly.
  constexpr char kIfaceDriverName[] = "nl80211";
  constexpr char kStaIfaceConfPath[] =
      "/data/vendor/wifi/wpa/wpa_supplicant.conf";
  constexpr char kStaIfaceConfOverlayPath[] =
      "/vendor/etc/wifi/wpa_supplicant_overlay.conf";
  constexpr char kP2pIfaceConfPath[] =
      "/data/vendor/wifi/wpa/p2p_supplicant.conf";
  constexpr char kP2pIfaceConfOverlayPath[] =
      "/vendor/etc/wifi/p2p_supplicant_overlay.conf";
  // Migrate conf files for existing devices.
  constexpr char kSystemTemplateConfPath[] =
      "/system/etc/wifi/wpa_supplicant.conf";
  constexpr char kVendorTemplateConfPath[] =
      "/vendor/etc/wifi/wpa_supplicant.conf";
  constexpr char kOldStaIfaceConfPath[] = "/data/misc/wifi/wpa_supplicant.conf";
  constexpr char kOldP2pIfaceConfPath[] = "/data/misc/wifi/p2p_supplicant.conf";
  constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
  
  int copyFile(
      const std::string& src_file_path, const std::string& dest_file_path)
  {
  	std::string file_contents;
  	if (!android::base::ReadFileToString(src_file_path, &file_contents)) {
  		wpa_printf(
  		    MSG_ERROR, "Failed to read from %s. Errno: %s",
  		    src_file_path.c_str(), strerror(errno));
  		return -1;
  	}
  	if (!android::base::WriteStringToFile(
  		file_contents, dest_file_path, kConfigFileMode, getuid(),
  		getgid())) {
  		wpa_printf(
  		    MSG_ERROR, "Failed to write to %s. Errno: %s",
  		    dest_file_path.c_str(), strerror(errno));
  		return -1;
  	}
  	return 0;
  }
  
  /**
   * Copy |src_file_path| to |dest_file_path| if it exists.
   *
   * Returns 1 if |src_file_path| does not exist or not accessible,
   * Returns -1 if the copy fails.
   * Returns 0 if the copy succeeds.
   */
  int copyFileIfItExists(
      const std::string& src_file_path, const std::string& dest_file_path)
  {
  	int ret = access(src_file_path.c_str(), R_OK);
  	// Sepolicy denial (2018+ device) will return EACCESS instead of ENOENT.
  	if ((ret != 0) && ((errno == ENOENT) || (errno == EACCES))) {
  		return 1;
  	}
  	ret = copyFile(src_file_path, dest_file_path);
  	if (ret != 0) {
  		wpa_printf(
  		    MSG_ERROR, "Failed copying %s to %s.",
  		    src_file_path.c_str(), dest_file_path.c_str());
  		return -1;
  	}
  	return 0;
  }
  
  /**
   * Ensure that the specified config file pointed by |config_file_path| exists.
   * a) If the |config_file_path| exists with the correct permissions, return.
   * b) If the |config_file_path| does not exist, but |old_config_file_path|
   * exists, copy over the contents of the |old_config_file_path| to
   * |config_file_path|.
   * c) If the |config_file_path| & |old_config_file_path|
   * does not exists, copy over the contents of |template_config_file_path|.
   */
  int ensureConfigFileExists(
      const std::string& config_file_path,
      const std::string& old_config_file_path)
  {
  	int ret = access(config_file_path.c_str(), R_OK | W_OK);
  	if (ret == 0) {
  		return 0;
  	}
  	if (errno == EACCES) {
  		ret = chmod(config_file_path.c_str(), kConfigFileMode);
  		if (ret == 0) {
  			return 0;
  		} else {
  			wpa_printf(
  			    MSG_ERROR, "Cannot set RW to %s. Errno: %s",
  			    config_file_path.c_str(), strerror(errno));
  			return -1;
  		}
  	} else if (errno != ENOENT) {
  		wpa_printf(
  		    MSG_ERROR, "Cannot acces %s. Errno: %s",
  		    config_file_path.c_str(), strerror(errno));
  		return -1;
  	}
  	ret = copyFileIfItExists(old_config_file_path, config_file_path);
  	if (ret == 0) {
  		wpa_printf(
  		    MSG_INFO, "Migrated conf file from %s to %s",
  		    old_config_file_path.c_str(), config_file_path.c_str());
  		unlink(old_config_file_path.c_str());
  		return 0;
  	} else if (ret == -1) {
  		unlink(config_file_path.c_str());
  		return -1;
  	}
  	ret = copyFileIfItExists(kVendorTemplateConfPath, config_file_path);
  	if (ret == 0) {
  		wpa_printf(
  		    MSG_INFO, "Copied template conf file from %s to %s",
  		    kVendorTemplateConfPath, config_file_path.c_str());
  		return 0;
  	} else if (ret == -1) {
  		unlink(config_file_path.c_str());
  		return -1;
  	}
  	ret = copyFileIfItExists(kSystemTemplateConfPath, config_file_path);
  	if (ret == 0) {
  		wpa_printf(
  		    MSG_INFO, "Copied template conf file from %s to %s",
  		    kSystemTemplateConfPath, config_file_path.c_str());
  		return 0;
  	} else if (ret == -1) {
  		unlink(config_file_path.c_str());
  		return -1;
  	}
  	// Did not create the conf file.
  	return -1;
  }
  }  // namespace
  
  namespace android {
  namespace hardware {
  namespace wifi {
  namespace supplicant {
  namespace V1_2 {
  namespace implementation {
  using hidl_return_util::validateAndCall;
  
  Supplicant::Supplicant(struct wpa_global* global) : wpa_global_(global) {}
  bool Supplicant::isValid()
  {
  	// This top level object cannot be invalidated.
  	return true;
  }
  
  Return<void> Supplicant::addInterface(
      const IfaceInfo& iface_info, addInterface_cb _hidl_cb)
  {
  	return validateAndCall(
  	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
  	    &Supplicant::addInterfaceInternal, _hidl_cb, iface_info);
  }
  
  Return<void> Supplicant::removeInterface(
      const IfaceInfo& iface_info, removeInterface_cb _hidl_cb)
  {
  	return validateAndCall(
  	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
  	    &Supplicant::removeInterfaceInternal, _hidl_cb, iface_info);
  }
  
  Return<void> Supplicant::getInterface(
      const IfaceInfo& iface_info, getInterface_cb _hidl_cb)
  {
  	return validateAndCall(
  	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
  	    &Supplicant::getInterfaceInternal, _hidl_cb, iface_info);
  }
  
  Return<void> Supplicant::listInterfaces(listInterfaces_cb _hidl_cb)
  {
  	return validateAndCall(
  	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
  	    &Supplicant::listInterfacesInternal, _hidl_cb);
  }
  
  Return<void> Supplicant::registerCallback(
      const sp<ISupplicantCallback>& callback, registerCallback_cb _hidl_cb)
  {
  	return validateAndCall(
  	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
  	    &Supplicant::registerCallbackInternal, _hidl_cb, callback);
  }
  
  Return<void> Supplicant::setDebugParams(
      ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys,
      setDebugParams_cb _hidl_cb)
  {
  	return validateAndCall(
  	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
  	    &Supplicant::setDebugParamsInternal, _hidl_cb, level,
  	    show_timestamp, show_keys);
  }
  
  Return<void> Supplicant::setConcurrencyPriority(
      IfaceType type, setConcurrencyPriority_cb _hidl_cb)
  {
  	return validateAndCall(
  	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
  	    &Supplicant::setConcurrencyPriorityInternal, _hidl_cb, type);
  }
  
  Return<ISupplicant::DebugLevel> Supplicant::getDebugLevel()
  {
  	// TODO: Add SupplicantStatus in this method return for uniformity with
  	// the other methods in supplicant HIDL interface.
  	return (ISupplicant::DebugLevel)wpa_debug_level;
  }
  
  Return<bool> Supplicant::isDebugShowTimestampEnabled()
  {
  	// TODO: Add SupplicantStatus in this method return for uniformity with
  	// the other methods in supplicant HIDL interface.
  	return ((wpa_debug_timestamp != 0) ? true : false);
  }
  
  Return<bool> Supplicant::isDebugShowKeysEnabled()
  {
  	// TODO: Add SupplicantStatus in this method return for uniformity with
  	// the other methods in supplicant HIDL interface.
  	return ((wpa_debug_show_keys != 0) ? true : false);
  }
  
  Return<void> Supplicant::terminate()
  {
  	wpa_printf(MSG_INFO, "Terminating...");
  	wpa_supplicant_terminate_proc(wpa_global_);
  	return Void();
  }
  
  std::pair<SupplicantStatus, sp<ISupplicantIface>>
  Supplicant::addInterfaceInternal(const IfaceInfo& iface_info)
  {
  	android::sp<ISupplicantIface> iface;
  
  	// Check if required |ifname| argument is empty.
  	if (iface_info.name.empty()) {
  		return {{SupplicantStatusCode::FAILURE_ARGS_INVALID, ""}, {}};
  	}
  	// Try to get the wpa_supplicant record for this iface, return
  	// the iface object with the appropriate status code if it exists.
  	SupplicantStatus status;
  	std::tie(status, iface) = getInterfaceInternal(iface_info);
  	if (status.code == SupplicantStatusCode::SUCCESS) {
  		return {{SupplicantStatusCode::FAILURE_IFACE_EXISTS, ""},
  			iface};
  	}
  
  	struct wpa_interface iface_params = {};
  	iface_params.driver = kIfaceDriverName;
  	if (iface_info.type == IfaceType::P2P) {
  		if (ensureConfigFileExists(
  			kP2pIfaceConfPath, kOldP2pIfaceConfPath) != 0) {
  			wpa_printf(
  			    MSG_ERROR, "Conf file does not exists: %s",
  			    kP2pIfaceConfPath);
  			return {{SupplicantStatusCode::FAILURE_UNKNOWN,
  				 "Conf file does not exist"},
  				{}};
  		}
  		iface_params.confname = kP2pIfaceConfPath;
  		int ret = access(kP2pIfaceConfOverlayPath, R_OK);
  		if (ret == 0) {
  			iface_params.confanother = kP2pIfaceConfOverlayPath;
  		}
  	} else {
  		if (ensureConfigFileExists(
  			kStaIfaceConfPath, kOldStaIfaceConfPath) != 0) {
  			wpa_printf(
  			    MSG_ERROR, "Conf file does not exists: %s",
  			    kStaIfaceConfPath);
  			return {{SupplicantStatusCode::FAILURE_UNKNOWN,
  				 "Conf file does not exist"},
  				{}};
  		}
  		iface_params.confname = kStaIfaceConfPath;
  		int ret = access(kStaIfaceConfOverlayPath, R_OK);
  		if (ret == 0) {
  			iface_params.confanother = kStaIfaceConfOverlayPath;
  		}
  	}
  	iface_params.ifname = iface_info.name.c_str();
  	struct wpa_supplicant* wpa_s =
  	    wpa_supplicant_add_iface(wpa_global_, &iface_params, NULL);
  	if (!wpa_s) {
  		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
  	}
  	// The supplicant core creates a corresponding hidl object via
  	// HidlManager when |wpa_supplicant_add_iface| is called.
  	return getInterfaceInternal(iface_info);
  }
  
  SupplicantStatus Supplicant::removeInterfaceInternal(
      const IfaceInfo& iface_info)
  {
  	struct wpa_supplicant* wpa_s =
  	    wpa_supplicant_get_iface(wpa_global_, iface_info.name.c_str());
  	if (!wpa_s) {
  		return {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""};
  	}
  	if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0)) {
  		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
  	}
  	return {SupplicantStatusCode::SUCCESS, ""};
  }
  
  std::pair<SupplicantStatus, sp<ISupplicantIface>>
  Supplicant::getInterfaceInternal(const IfaceInfo& iface_info)
  {
  	struct wpa_supplicant* wpa_s =
  	    wpa_supplicant_get_iface(wpa_global_, iface_info.name.c_str());
  	if (!wpa_s) {
  		return {{SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""},
  			nullptr};
  	}
  	HidlManager* hidl_manager = HidlManager::getInstance();
  	if (iface_info.type == IfaceType::P2P) {
  		android::sp<ISupplicantP2pIface> iface;
  		if (!hidl_manager ||
  		    hidl_manager->getP2pIfaceHidlObjectByIfname(
  			wpa_s->ifname, &iface)) {
  			return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""},
  				iface};
  		}
  		// Set this flag true here, since there is no HIDL initialize
  		// method for the p2p config, and the supplicant interface is
  		// not ready when the p2p iface is created.
  		wpa_s->conf->persistent_reconnect = true;
  		return {{SupplicantStatusCode::SUCCESS, ""}, iface};
  	} else {
  		android::sp<ISupplicantStaIface> iface;
  		if (!hidl_manager ||
  		    hidl_manager->getStaIfaceHidlObjectByIfname(
  			wpa_s->ifname, &iface)) {
  			return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""},
  				iface};
  		}
  		return {{SupplicantStatusCode::SUCCESS, ""}, iface};
  	}
  }
  
  std::pair<SupplicantStatus, std::vector<ISupplicant::IfaceInfo>>
  Supplicant::listInterfacesInternal()
  {
  	std::vector<ISupplicant::IfaceInfo> ifaces;
  	for (struct wpa_supplicant* wpa_s = wpa_global_->ifaces; wpa_s;
  	     wpa_s = wpa_s->next) {
  		if (wpa_s->global->p2p_init_wpa_s == wpa_s) {
  			ifaces.emplace_back(ISupplicant::IfaceInfo{
  			    IfaceType::P2P, wpa_s->ifname});
  		} else {
  			ifaces.emplace_back(ISupplicant::IfaceInfo{
  			    IfaceType::STA, wpa_s->ifname});
  		}
  	}
  	return {{SupplicantStatusCode::SUCCESS, ""}, std::move(ifaces)};
  }
  
  SupplicantStatus Supplicant::registerCallbackInternal(
      const sp<ISupplicantCallback>& callback)
  {
  	HidlManager* hidl_manager = HidlManager::getInstance();
  	if (!hidl_manager ||
  	    hidl_manager->addSupplicantCallbackHidlObject(callback)) {
  		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
  	}
  	return {SupplicantStatusCode::SUCCESS, ""};
  }
  
  SupplicantStatus Supplicant::setDebugParamsInternal(
      ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys)
  {
  	if (wpa_supplicant_set_debug_params(
  		wpa_global_, static_cast<uint32_t>(level), show_timestamp,
  		show_keys)) {
  		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
  	}
  	return {SupplicantStatusCode::SUCCESS, ""};
  }
  
  SupplicantStatus Supplicant::setConcurrencyPriorityInternal(IfaceType type)
  {
  	if (type == IfaceType::STA) {
  		wpa_global_->conc_pref =
  		    wpa_global::wpa_conc_pref::WPA_CONC_PREF_STA;
  	} else if (type == IfaceType::P2P) {
  		wpa_global_->conc_pref =
  		    wpa_global::wpa_conc_pref::WPA_CONC_PREF_P2P;
  	} else {
  		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
  	}
  	return SupplicantStatus{SupplicantStatusCode::SUCCESS, ""};
  }
  }  // namespace implementation
  }  // namespace V1_2
  }  // namespace supplicant
  }  // namespace wifi
  }  // namespace hardware
  }  // namespace android
  