// // Copyright (C) 2012 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__ANDROID__) #include #else #include #endif // __ANDROID__ #include "shill/adaptor_interfaces.h" #include "shill/callbacks.h" #include "shill/connection.h" #include "shill/control_interface.h" #include "shill/default_profile.h" #include "shill/device.h" #include "shill/device_claimer.h" #include "shill/device_info.h" #include "shill/ephemeral_profile.h" #include "shill/error.h" #include "shill/ethernet/ethernet_temporary_service.h" #include "shill/event_dispatcher.h" #include "shill/geolocation_info.h" #include "shill/hook_table.h" #include "shill/ip_address_store.h" #include "shill/logging.h" #include "shill/profile.h" #include "shill/property_accessor.h" #include "shill/resolver.h" #include "shill/result_aggregator.h" #include "shill/service.h" #include "shill/service_sorter.h" #include "shill/store_factory.h" #include "shill/vpn/vpn_provider.h" #include "shill/vpn/vpn_service.h" #include "shill/wimax/wimax_service.h" #if defined(__BRILLO__) #include "shill/wifi/wifi_driver_hal.h" #endif // __BRILLO__ #if !defined(DISABLE_WIFI) #include "shill/wifi/wifi.h" #include "shill/wifi/wifi_provider.h" #include "shill/wifi/wifi_service.h" #endif // DISABLE_WIFI #if !defined(DISABLE_WIRED_8021X) #include "shill/ethernet/ethernet_eap_provider.h" #include "shill/ethernet/ethernet_eap_service.h" #endif // DISABLE_WIRED_8021X using base::Bind; using base::Callback; using base::FilePath; using base::StringPrintf; using base::Unretained; using std::map; using std::set; using std::string; using std::vector; namespace shill { namespace Logging { static auto kModuleLogScope = ScopeLogger::kManager; static string ObjectID(const Manager* m) { return "manager"; } } // statics const char Manager::kErrorNoDevice[] = "no wifi devices available"; const char Manager::kErrorTypeRequired[] = "must specify service type"; const char Manager::kErrorUnsupportedServiceType[] = "service type is unsupported"; // This timeout should be less than the upstart job timeout, otherwise // stats for termination actions might be lost. const int Manager::kTerminationActionsTimeoutMilliseconds = 19500; // Device status check interval (every 3 minutes). const int Manager::kDeviceStatusCheckIntervalMilliseconds = 180000; // static const char* Manager::kProbeTechnologies[] = { kTypeEthernet, kTypeWifi, kTypeWimax, kTypeCellular }; // static const char Manager::kDefaultClaimerName[] = ""; Manager::Manager(ControlInterface* control_interface, EventDispatcher* dispatcher, Metrics* metrics, const string& run_directory, const string& storage_directory, const string& user_storage_directory) : dispatcher_(dispatcher), run_path_(FilePath(run_directory)), storage_path_(FilePath(storage_directory)), user_storage_path_(user_storage_directory), user_profile_list_path_(FilePath(Profile::kUserProfileListPathname)), adaptor_(control_interface->CreateManagerAdaptor(this)), device_info_(control_interface, dispatcher, metrics, this), #if !defined(DISABLE_CELLULAR) modem_info_(control_interface, dispatcher, metrics, this), #endif // DISABLE_CELLULAR #if !defined(DISABLE_WIRED_8021X) ethernet_eap_provider_( new EthernetEapProvider( control_interface, dispatcher, metrics, this)), #endif // DISABLE_WIRED_8021X vpn_provider_( new VPNProvider(control_interface, dispatcher, metrics, this)), #if !defined(DISABLE_WIFI) wifi_provider_( new WiFiProvider(control_interface, dispatcher, metrics, this)), #if defined(__BRILLO__) wifi_driver_hal_(WiFiDriverHal::GetInstance()), #endif // __BRILLO__ #endif // DISABLE_WIFI #if !defined(DISABLE_WIMAX) wimax_provider_( new WiMaxProvider(control_interface, dispatcher, metrics, this)), #endif // DISABLE_WIMAX resolver_(Resolver::GetInstance()), running_(false), connect_profiles_to_rpc_(true), ephemeral_profile_( new EphemeralProfile(control_interface, metrics, this)), control_interface_(control_interface), metrics_(metrics), use_startup_portal_list_(false), device_status_check_task_(Bind(&Manager::DeviceStatusCheckTask, base::Unretained(this))), termination_actions_(dispatcher), suspend_delay_registered_(false), is_wake_on_lan_enabled_(true), ignore_unknown_ethernet_(false), default_service_callback_tag_(0), crypto_util_proxy_(new CryptoUtilProxy(dispatcher)), health_checker_remote_ips_(new IPAddressStore()), suppress_autoconnect_(false), is_connected_state_(false), dhcp_properties_(new DhcpProperties()) { HelpRegisterDerivedString(kActiveProfileProperty, &Manager::GetActiveProfileRpcIdentifier, nullptr); store_.RegisterBool(kArpGatewayProperty, &props_.arp_gateway); HelpRegisterConstDerivedStrings(kAvailableTechnologiesProperty, &Manager::AvailableTechnologies); HelpRegisterDerivedString(kCheckPortalListProperty, &Manager::GetCheckPortalList, &Manager::SetCheckPortalList); HelpRegisterConstDerivedStrings(kConnectedTechnologiesProperty, &Manager::ConnectedTechnologies); store_.RegisterConstString(kConnectionStateProperty, &connection_state_); store_.RegisterString(kCountryProperty, &props_.country); HelpRegisterDerivedString(kDefaultTechnologyProperty, &Manager::DefaultTechnology, nullptr); HelpRegisterConstDerivedRpcIdentifier( kDefaultServiceProperty, &Manager::GetDefaultServiceRpcIdentifier); HelpRegisterConstDerivedRpcIdentifiers(kDevicesProperty, &Manager::EnumerateDevices); #if !defined(DISABLE_WIFI) HelpRegisterDerivedBool(kDisableWiFiVHTProperty, &Manager::GetDisableWiFiVHT, &Manager::SetDisableWiFiVHT); #endif // DISABLE_WIFI HelpRegisterConstDerivedStrings(kEnabledTechnologiesProperty, &Manager::EnabledTechnologies); HelpRegisterDerivedString(kIgnoredDNSSearchPathsProperty, &Manager::GetIgnoredDNSSearchPaths, &Manager::SetIgnoredDNSSearchPaths); store_.RegisterString(kHostNameProperty, &props_.host_name); store_.RegisterString(kLinkMonitorTechnologiesProperty, &props_.link_monitor_technologies); store_.RegisterString(kNoAutoConnectTechnologiesProperty, &props_.no_auto_connect_technologies); store_.RegisterBool(kOfflineModeProperty, &props_.offline_mode); store_.RegisterString(kPortalURLProperty, &props_.portal_url); store_.RegisterInt32(kPortalCheckIntervalProperty, &props_.portal_check_interval_seconds); HelpRegisterConstDerivedRpcIdentifiers(kProfilesProperty, &Manager::EnumerateProfiles); HelpRegisterDerivedString(kProhibitedTechnologiesProperty, &Manager::GetProhibitedTechnologies, &Manager::SetProhibitedTechnologies); HelpRegisterDerivedString(kStateProperty, &Manager::CalculateState, nullptr); HelpRegisterConstDerivedRpcIdentifiers(kServicesProperty, &Manager::EnumerateAvailableServices); HelpRegisterConstDerivedRpcIdentifiers(kServiceCompleteListProperty, &Manager::EnumerateCompleteServices); HelpRegisterConstDerivedRpcIdentifiers(kServiceWatchListProperty, &Manager::EnumerateWatchedServices); HelpRegisterConstDerivedStrings(kUninitializedTechnologiesProperty, &Manager::UninitializedTechnologies); store_.RegisterBool(kWakeOnLanEnabledProperty, &is_wake_on_lan_enabled_); HelpRegisterConstDerivedStrings(kClaimedDevicesProperty, &Manager::ClaimedDevices); UpdateProviderMapping(); dhcp_properties_->InitPropertyStore(&store_); SLOG(this, 2) << "Manager initialized."; } Manager::~Manager() {} void Manager::RegisterAsync(const Callback& completion_callback) { adaptor_->RegisterAsync(completion_callback); } void Manager::SetBlacklistedDevices(const vector& blacklisted_devices) { blacklisted_devices_ = blacklisted_devices; } void Manager::SetWhitelistedDevices(const vector& whitelisted_devices) { whitelisted_devices_ = whitelisted_devices; } void Manager::Start() { LOG(INFO) << "Manager started."; power_manager_.reset( new PowerManager(dispatcher_, control_interface_)); power_manager_->Start(base::TimeDelta::FromMilliseconds( kTerminationActionsTimeoutMilliseconds), Bind(&Manager::OnSuspendImminent, AsWeakPtr()), Bind(&Manager::OnSuspendDone, AsWeakPtr()), Bind(&Manager::OnDarkSuspendImminent, AsWeakPtr())); upstart_.reset(new Upstart(control_interface_)); CHECK(base::CreateDirectory(run_path_)) << run_path_.value(); resolver_->set_path(run_path_.Append("resolv.conf")); InitializeProfiles(); running_ = true; device_info_.Start(); #if !defined(DISABLE_CELLULAR) modem_info_.Start(); #endif // DISABLE_CELLULAR for (const auto& provider_mapping : providers_) { provider_mapping.second->Start(); } // Start task for checking connection status. dispatcher_->PostDelayedTask(device_status_check_task_.callback(), kDeviceStatusCheckIntervalMilliseconds); } void Manager::Stop() { running_ = false; // Persist device information to disk; for (const auto& device : devices_) { UpdateDevice(device); } #if !defined(DISABLE_WIFI) UpdateWiFiProvider(); #endif // DISABLE_WIFI // Persist profile, service information to disk. for (const auto& profile : profiles_) { // Since this happens in a loop, the current manager state is stored to // all default profiles in the stack. This is acceptable because the // only time multiple default profiles are loaded are during autotests. profile->Save(); } Error e; for (const auto& service : services_) { service->Disconnect(&e, __func__); } for (const auto& device : devices_) { device->SetEnabled(false); } for (const auto& provider_mapping : providers_) { provider_mapping.second->Stop(); } #if !defined(DISABLE_CELLULAR) modem_info_.Stop(); #endif // DISABLE_CELLULAR device_info_.Stop(); device_status_check_task_.Cancel(); sort_services_task_.Cancel(); power_manager_->Stop(); power_manager_.reset(); } void Manager::InitializeProfiles() { DCHECK(profiles_.empty()); // The default profile must go first on stack. CHECK(base::CreateDirectory(storage_path_)) << storage_path_.value(); // Ensure that we have storage for the default profile, and that // the persistent copy of the default profile is not corrupt. scoped_refptr default_profile(new DefaultProfile(control_interface_, metrics_, this, storage_path_, DefaultProfile::kDefaultId, props_)); // The default profile may fail to initialize if it's corrupted. // If so, recreate the default profile. if (!default_profile->InitStorage(Profile::kCreateOrOpenExisting, nullptr)) CHECK(default_profile->InitStorage(Profile::kCreateNew, nullptr)); // In case we created a new profile, initialize its default values, // and then save. This is required for properties such as // PortalDetector::kDefaultCheckPortalList to be initialized correctly. LoadProperties(default_profile); default_profile->Save(); default_profile = nullptr; // PushProfileInternal will re-create. // Read list of user profiles. This must be done before pushing the // default profile, because modifying the profile stack updates the // user profile list. vector identifiers = Profile::LoadUserProfileList(user_profile_list_path_); // Push the default profile onto the stack. Error error; string path; Profile::Identifier default_profile_id; CHECK(Profile::ParseIdentifier( DefaultProfile::kDefaultId, &default_profile_id)); PushProfileInternal(default_profile_id, &path, &error); CHECK(!profiles_.empty()); // Must have a default profile. // Push user profiles onto the stack. for (const auto& profile_id : identifiers) { PushProfileInternal(profile_id, &path, &error); } } void Manager::CreateProfile(const string& name, string* path, Error* error) { SLOG(this, 2) << __func__ << " " << name; Profile::Identifier ident; if (!Profile::ParseIdentifier(name, &ident)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Invalid profile name " + name); return; } if (HasProfile(ident)) { Error::PopulateAndLog(FROM_HERE, error, Error::kAlreadyExists, "Profile name " + name + " is already on stack"); return; } ProfileRefPtr profile; if (ident.user.empty()) { profile = new DefaultProfile(control_interface_, metrics_, this, storage_path_, ident.identifier, props_); } else { profile = new Profile(control_interface_, metrics_, this, ident, user_storage_path_, true); } if (!profile->InitStorage(Profile::kCreateNew, error)) { // |error| will have been populated by InitStorage(). return; } // Save profile data out, and then let the scoped pointer fall out of scope. if (!profile->Save()) { Error::PopulateAndLog(FROM_HERE, error, Error::kInternalError, "Profile name " + name + " could not be saved"); return; } *path = profile->GetRpcIdentifier(); } bool Manager::HasProfile(const Profile::Identifier& ident) { for (const auto& profile : profiles_) { if (profile->MatchesIdentifier(ident)) { return true; } } return false; } void Manager::PushProfileInternal( const Profile::Identifier& ident, string* path, Error* error) { if (HasProfile(ident)) { Error::PopulateAndLog(FROM_HERE, error, Error::kAlreadyExists, "Profile name " + Profile::IdentifierToString(ident) + " is already on stack"); return; } ProfileRefPtr profile; if (ident.user.empty()) { // Allow a machine-wide-profile to be pushed on the stack only if the // profile stack is empty, or if the topmost profile on the stack is // also a machine-wide (non-user) profile. if (!profiles_.empty() && !profiles_.back()->GetUser().empty()) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Cannot load non-default global profile " + Profile::IdentifierToString(ident) + " on top of a user profile"); return; } scoped_refptr default_profile(new DefaultProfile(control_interface_, metrics_, this, storage_path_, ident.identifier, props_)); if (!default_profile->InitStorage(Profile::kOpenExisting, nullptr)) { LOG(ERROR) << "Failed to open default profile."; // Try to continue anyway, so that we can be useful in cases // where the disk is full. default_profile->InitStubStorage(); } LoadProperties(default_profile); profile = default_profile; } else { profile = new Profile(control_interface_, metrics_, this, ident, user_storage_path_, connect_profiles_to_rpc_); if (!profile->InitStorage(Profile::kOpenExisting, error)) { // |error| will have been populated by InitStorage(). return; } } profiles_.push_back(profile); for (ServiceRefPtr& service : services_) { service->ClearExplicitlyDisconnected(); // Offer each registered Service the opportunity to join this new Profile. if (profile->ConfigureService(service)) { LOG(INFO) << "(Re-)configured service " << service->unique_name() << " from new profile."; } } // Shop the Profile contents around to Devices which may have configuration // stored in these profiles. for (DeviceRefPtr& device : devices_) { profile->ConfigureDevice(device); } // Offer the Profile contents to the service providers which will // create new services if necessary. for (const auto& provider_mapping : providers_) { provider_mapping.second->CreateServicesFromProfile(profile); } *path = profile->GetRpcIdentifier(); SortServices(); OnProfilesChanged(); LOG(INFO) << __func__ << " finished; " << profiles_.size() << " profile(s) now present."; } void Manager::PushProfile(const string& name, string* path, Error* error) { SLOG(this, 2) << __func__ << " " << name; Profile::Identifier ident; if (!Profile::ParseIdentifier(name, &ident)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Invalid profile name " + name); return; } PushProfileInternal(ident, path, error); } void Manager::InsertUserProfile(const string& name, const string& user_hash, string* path, Error* error) { SLOG(this, 2) << __func__ << " " << name; Profile::Identifier ident; if (!Profile::ParseIdentifier(name, &ident) || ident.user.empty()) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Invalid user profile name " + name); return; } ident.user_hash = user_hash; PushProfileInternal(ident, path, error); } void Manager::PopProfileInternal() { CHECK(!profiles_.empty()); ProfileRefPtr active_profile = profiles_.back(); profiles_.pop_back(); for (auto it = services_.begin(); it != services_.end();) { (*it)->ClearExplicitlyDisconnected(); if (IsServiceEphemeral(*it)) { // Not affected, since the EphemeralProfile isn't on the stack. // Not logged, since ephemeral services aren't that interesting. ++it; continue; } if ((*it)->profile().get() != active_profile.get()) { LOG(INFO) << "Skipping unload of service " << (*it)->unique_name() << ": wasn't using this profile."; ++it; continue; } if (MatchProfileWithService(*it)) { LOG(INFO) << "Skipping unload of service " << (*it)->unique_name() << ": re-configured from another profile."; ++it; continue; } if (!UnloadService(&it)) { LOG(INFO) << "Service " << (*it)->unique_name() << " not completely unloaded."; ++it; continue; } // Service was totally unloaded. No advance of iterator in this // case, as UnloadService has updated the iterator for us. } SortServices(); OnProfilesChanged(); LOG(INFO) << __func__ << " finished; " << profiles_.size() << " profile(s) still present."; } void Manager::OnProfilesChanged() { Error unused_error; adaptor_->EmitStringsChanged(kProfilesProperty, EnumerateProfiles(&unused_error)); Profile::SaveUserProfileList(user_profile_list_path_, profiles_); } void Manager::PopProfile(const string& name, Error* error) { SLOG(this, 2) << __func__ << " " << name; Profile::Identifier ident; if (profiles_.empty()) { Error::PopulateAndLog( FROM_HERE, error, Error::kNotFound, "Profile stack is empty"); return; } ProfileRefPtr active_profile = profiles_.back(); if (!Profile::ParseIdentifier(name, &ident)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Invalid profile name " + name); return; } if (!active_profile->MatchesIdentifier(ident)) { Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported, name + " is not the active profile"); return; } PopProfileInternal(); } void Manager::PopAnyProfile(Error* error) { SLOG(this, 2) << __func__; Profile::Identifier ident; if (profiles_.empty()) { Error::PopulateAndLog( FROM_HERE, error, Error::kNotFound, "Profile stack is empty"); return; } PopProfileInternal(); } void Manager::PopAllUserProfiles(Error* /*error*/) { SLOG(this, 2) << __func__; while (!profiles_.empty() && !profiles_.back()->GetUser().empty()) { PopProfileInternal(); } } void Manager::RemoveProfile(const string& name, Error* error) { Profile::Identifier ident; if (!Profile::ParseIdentifier(name, &ident)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Invalid profile name " + name); return; } if (HasProfile(ident)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Cannot remove profile name " + name + " since it is on stack"); return; } ProfileRefPtr profile; if (ident.user.empty()) { profile = new DefaultProfile(control_interface_, metrics_, this, storage_path_, ident.identifier, props_); } else { profile = new Profile(control_interface_, metrics_, this, ident, user_storage_path_, false); } // |error| will have been populated if RemoveStorage fails. profile->RemoveStorage(error); return; } bool Manager::DeviceManagementAllowed(const string& device_name) { if (std::find(blacklisted_devices_.begin(), blacklisted_devices_.end(), device_name) != blacklisted_devices_.end()) { return false; } if (!whitelisted_devices_.size()) { // If no whitelist is specified, all devices are considered whitelisted. return true; } if (std::find(whitelisted_devices_.begin(), whitelisted_devices_.end(), device_name) != whitelisted_devices_.end()) { return true; } return false; } void Manager::ClaimDevice(const string& claimer_name, const string& device_name, Error* error) { SLOG(this, 2) << __func__; // Basic check for device name. if (device_name.empty()) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Empty device name"); return; } if (!DeviceManagementAllowed(device_name)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Not allowed to claim unmanaged device"); return; } // Verify default claimer. if (claimer_name.empty() && (!device_claimer_ || !device_claimer_->default_claimer())) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "No default claimer"); return; } // Create a new device claimer if one doesn't exist yet. if (!device_claimer_) { // Start a device claimer. No need to verify the existence of the claimer, // since we are using message sender as the claimer name. device_claimer_.reset( new DeviceClaimer(claimer_name, &device_info_, false)); } // Verify claimer's name, since we only allow one claimer to exist at a time. if (device_claimer_->name() != claimer_name) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Invalid claimer name " + claimer_name + ". Claimer " + device_claimer_->name() + " already exist"); return; } // Error will be populated by the claimer if failed to claim the device. if (!device_claimer_->Claim(device_name, error)) { return; } // Deregister the device from manager if it is registered. DeregisterDeviceByLinkName(device_name); } void Manager::ReleaseDevice(const string& claimer_name, const string& device_name, bool* claimer_removed, Error* error) { SLOG(this, 2) << __func__; *claimer_removed = false; if (!DeviceManagementAllowed(device_name)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Not allowed to release unmanaged device"); return; } if (!device_claimer_) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Device claimer doesn't exist"); return; } // Verify claimer's name, since we only allow one claimer to exist at a time. if (device_claimer_->name() != claimer_name) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Invalid claimer name " + claimer_name + ". Claimer " + device_claimer_->name() + " already exist"); return; } // Release the device from the claimer. Error should be populated by the // claimer if it failed to release the given device. device_claimer_->Release(device_name, error); // Reset claimer if this is not the default claimer and no more devices are // claimed by this claimer. if (!device_claimer_->default_claimer() && !device_claimer_->DevicesClaimed()) { device_claimer_.reset(); *claimer_removed = true; } } #if !defined(DISABLE_WIFI) && defined(__BRILLO__) bool Manager::SetupApModeInterface(string* out_interface_name, Error* error) { string interface_name = wifi_driver_hal_->SetupApModeInterface(); if (interface_name.empty()) { Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed, "Failed to setup AP mode interface"); return false; } *out_interface_name = interface_name; return true; } bool Manager::SetupStationModeInterface(string* out_interface_name, Error* error) { string interface_name = wifi_driver_hal_->SetupStationModeInterface(); if (interface_name.empty()) { Error::PopulateAndLog(FROM_HERE, error, Error::kOperationFailed, "Failed to setup station mode interface"); return false; } *out_interface_name = interface_name; return true; } void Manager::OnApModeSetterVanished() { // Restore station mode interface. string interface_name = wifi_driver_hal_->SetupStationModeInterface(); if (interface_name.empty()) { LOG(ERROR) << "Failed to restore station mode interface"; } } #endif // !DISABLE_WIFI && __BRILLO__ void Manager::RemoveService(const ServiceRefPtr& service) { LOG(INFO) << __func__ << " for service " << service->unique_name(); if (!IsServiceEphemeral(service)) { service->profile()->AbandonService(service); if (MatchProfileWithService(service)) { // We found another profile to adopt the service; no need to unload. UpdateService(service); return; } } auto service_it = std::find(services_.begin(), services_.end(), service); CHECK(service_it != services_.end()); if (!UnloadService(&service_it)) { UpdateService(service); } SortServices(); } bool Manager::HandleProfileEntryDeletion(const ProfileRefPtr& profile, const std::string& entry_name) { bool moved_services = false; for (auto it = services_.begin(); it != services_.end();) { if ((*it)->profile().get() == profile.get() && (*it)->GetStorageIdentifier() == entry_name) { profile->AbandonService(*it); if (MatchProfileWithService(*it) || !UnloadService(&it)) { ++it; } moved_services = true; } else { ++it; } } if (moved_services) { SortServices(); } return moved_services; } map Manager::GetLoadableProfileEntriesForService( const ServiceConstRefPtr& service) { map profile_entries; for (const auto& profile : profiles_) { string entry_name = service->GetLoadableStorageIdentifier( *profile->GetConstStorage()); if (!entry_name.empty()) { profile_entries[profile->GetRpcIdentifier()] = entry_name; } } return profile_entries; } ServiceRefPtr Manager::GetServiceWithStorageIdentifier( const ProfileRefPtr& profile, const std::string& entry_name, Error* error) { for (const auto& service : services_) { if (service->profile().get() == profile.get() && service->GetStorageIdentifier() == entry_name) { return service; } } SLOG(this, 2) << "Entry " << entry_name << " is not registered in the manager"; return nullptr; } ServiceRefPtr Manager::CreateTemporaryServiceFromProfile( const ProfileRefPtr& profile, const std::string& entry_name, Error* error) { Technology::Identifier technology = Technology::IdentifierFromStorageGroup(entry_name); if (technology == Technology::kUnknown) { Error::PopulateAndLog( FROM_HERE, error, Error::kInternalError, "Could not determine technology for entry: " + entry_name); return nullptr; } ServiceRefPtr service = nullptr; // Since there is no provider for Ethernet services (Ethernet services are // created/provided by the Ethernet device), we will explicitly create // temporary Ethernet services for loading Ethernet entries. if (technology == Technology::kEthernet) { service = new EthernetTemporaryService(control_interface_, dispatcher_, metrics_, this, entry_name); } else if (ContainsKey(providers_, technology)) { service = providers_[technology]->CreateTemporaryServiceFromProfile( profile, entry_name, error); } if (!service) { Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported, kErrorUnsupportedServiceType); return nullptr; } profile->LoadService(service); return service; } ServiceRefPtr Manager::GetServiceWithGUID( const std::string& guid, Error* error) { for (const auto& service : services_) { if (service->guid() == guid) { return service; } } string error_string( StringPrintf("Service wth GUID %s is not registered in the manager", guid.c_str())); if (error) { error->Populate(Error::kNotFound, error_string); } SLOG(this, 2) << error_string; return nullptr; } ServiceRefPtr Manager::GetDefaultService() const { SLOG(this, 2) << __func__; if (services_.empty() || !services_[0]->connection().get()) { SLOG(this, 2) << "In " << __func__ << ": No default connection exists."; return nullptr; } return services_[0]; } RpcIdentifier Manager::GetDefaultServiceRpcIdentifier(Error* /*error*/) { ServiceRefPtr default_service = GetDefaultService(); return default_service ? default_service->GetRpcIdentifier() : control_interface_->NullRPCIdentifier(); } bool Manager::IsTechnologyInList(const string& technology_list, Technology::Identifier tech) const { if (technology_list.empty()) return false; Error error; vector technologies; return Technology::GetTechnologyVectorFromString(technology_list, &technologies, &error) && std::find(technologies.begin(), technologies.end(), tech) != technologies.end(); } bool Manager::IsPortalDetectionEnabled(Technology::Identifier tech) { return IsTechnologyInList(GetCheckPortalList(nullptr), tech); } void Manager::SetStartupPortalList(const string& portal_list) { startup_portal_list_ = portal_list; use_startup_portal_list_ = true; } bool Manager::IsProfileBefore(const ProfileRefPtr& a, const ProfileRefPtr& b) const { DCHECK(a != b); for (const auto& profile : profiles_) { if (profile == a) { return true; } if (profile == b) { return false; } } NOTREACHED() << "We should have found both profiles in the profiles_ list!"; return false; } bool Manager::IsServiceEphemeral(const ServiceConstRefPtr& service) const { return service->profile() == ephemeral_profile_; } bool Manager::IsTechnologyLinkMonitorEnabled( Technology::Identifier technology) const { return IsTechnologyInList(props_.link_monitor_technologies, technology); } bool Manager::IsTechnologyAutoConnectDisabled( Technology::Identifier technology) const { return IsTechnologyInList(props_.no_auto_connect_technologies, technology); } bool Manager::IsTechnologyProhibited( Technology::Identifier technology) const { return IsTechnologyInList(props_.prohibited_technologies, technology); } void Manager::OnProfileStorageInitialized(Profile* profile) { #if !defined(DISABLE_WIFI) wifi_provider_->LoadAndFixupServiceEntries(profile); #endif // DISABLE_WIFI } DeviceRefPtr Manager::GetEnabledDeviceWithTechnology( Technology::Identifier technology) const { for (const auto& device : FilterByTechnology(technology)) { if (device->enabled()) { return device; } } return nullptr; } DeviceRefPtr Manager::GetEnabledDeviceByLinkName( const string& link_name) const { for (const auto& device : devices_) { if (device->link_name() == link_name) { if (!device->enabled()) { return nullptr; } return device; } } return nullptr; } const ProfileRefPtr& Manager::ActiveProfile() const { DCHECK_NE(profiles_.size(), 0U); return profiles_.back(); } bool Manager::IsActiveProfile(const ProfileRefPtr& profile) const { return (profiles_.size() > 0 && ActiveProfile().get() == profile.get()); } bool Manager::MoveServiceToProfile(const ServiceRefPtr& to_move, const ProfileRefPtr& destination) { const ProfileRefPtr from = to_move->profile(); SLOG(this, 2) << "Moving service " << to_move->unique_name() << " to profile " << destination->GetFriendlyName() << " from " << from->GetFriendlyName(); return destination->AdoptService(to_move) && from->AbandonService(to_move); } ProfileRefPtr Manager::LookupProfileByRpcIdentifier( const string& profile_rpcid) { for (const auto& profile : profiles_) { if (profile_rpcid == profile->GetRpcIdentifier()) { return profile; } } return nullptr; } void Manager::SetProfileForService(const ServiceRefPtr& to_set, const string& profile_rpcid, Error* error) { ProfileRefPtr profile = LookupProfileByRpcIdentifier(profile_rpcid); if (!profile) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, StringPrintf("Unknown Profile %s requested for " "Service", profile_rpcid.c_str())); return; } if (!to_set->profile()) { // We are being asked to set the profile property of a service that // has never been registered. Now is a good time to register it. RegisterService(to_set); } if (to_set->profile().get() == profile.get()) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Service is already connected to this profile"); } else if (!MoveServiceToProfile(to_set, profile)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInternalError, "Unable to move service to profile"); } } void Manager::SetEnabledStateForTechnology(const std::string& technology_name, bool enabled_state, bool persist, Error* error, const ResultCallback& callback) { CHECK(error); DCHECK(error->IsOngoing()); Technology::Identifier id = Technology::IdentifierFromName(technology_name); if (id == Technology::kUnknown) { error->Populate(Error::kInvalidArguments, "Unknown technology"); return; } if (enabled_state && IsTechnologyProhibited(id)) { error->Populate(Error::kPermissionDenied, "The " + technology_name + " technology is prohibited"); return; } bool deferred = false; auto result_aggregator(make_scoped_refptr(new ResultAggregator(callback))); for (auto& device : devices_) { if (device->technology() != id) continue; Error device_error(Error::kOperationInitiated); ResultCallback aggregator_callback( Bind(&ResultAggregator::ReportResult, result_aggregator)); if (persist) { device->SetEnabledPersistent( enabled_state, &device_error, aggregator_callback); } else { device->SetEnabledNonPersistent( enabled_state, &device_error, aggregator_callback); } if (device_error.IsOngoing()) { deferred = true; } else if (!error->IsFailure()) { // Report first failure. error->CopyFrom(device_error); } } if (deferred) { // Some device is handling this change asynchronously. Clobber any error // from another device, so that we can indicate the operation is still in // progress. error->Populate(Error::kOperationInitiated); } else if (error->IsOngoing()) { // |error| IsOngoing at entry to this method, but no device // |deferred|. Reset |error|, to indicate we're done. error->Reset(); } } void Manager::UpdateEnabledTechnologies() { Error error; adaptor_->EmitStringsChanged(kEnabledTechnologiesProperty, EnabledTechnologies(&error)); } void Manager::UpdateUninitializedTechnologies() { Error error; adaptor_->EmitStringsChanged(kUninitializedTechnologiesProperty, UninitializedTechnologies(&error)); } void Manager::SetPassiveMode() { CHECK(!device_claimer_); // Create a default device claimer to claim devices from shill as they're // detected. Devices will be managed by remote application, which will use // the default claimer to specify the devices for shill to manage. device_claimer_.reset( new DeviceClaimer(kDefaultClaimerName, &device_info_, true)); } void Manager::SetIgnoreUnknownEthernet(bool ignore) { LOG(INFO) << __func__ << "(" << ignore << ")"; ignore_unknown_ethernet_ = ignore; } void Manager::SetPrependDNSServers(const std::string& prepend_dns_servers) { props_.prepend_dns_servers = prepend_dns_servers; } void Manager::SetAcceptHostnameFrom(const string& hostname_from) { accept_hostname_from_ = hostname_from; } bool Manager::ShouldAcceptHostnameFrom(const string& device_name) const { return base::MatchPattern(device_name, accept_hostname_from_); } void Manager::SetDHCPv6EnabledDevices(const vector& device_list) { dhcpv6_enabled_devices_ = device_list; } bool Manager::IsDHCPv6EnabledForDevice(const string& device_name) const { return std::find(dhcpv6_enabled_devices_.begin(), dhcpv6_enabled_devices_.end(), device_name) != dhcpv6_enabled_devices_.end(); } vector Manager::FilterPrependDNSServersByFamily( IPAddress::Family family) const { vector dns_servers; vector split_servers = base::SplitString( props_.prepend_dns_servers, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); for (const auto& server : split_servers) { const IPAddress address(server); if (address.family() == family) { dns_servers.push_back(server); } } return dns_servers; } bool Manager::IsSuspending() { if (power_manager_ && power_manager_->suspending()) { return true; } return false; } void Manager::RecordDarkResumeWakeReason(const string& wake_reason) { power_manager_->RecordDarkResumeWakeReason(wake_reason); } void Manager::RegisterDevice(const DeviceRefPtr& to_manage) { LOG(INFO) << "Device " << to_manage->FriendlyName() << " registered."; // Manager is running in passive mode when default claimer is created, which // means devices are being managed by remote application. Only manage the // device if it was explicitly released by remote application through // default claimer. if (device_claimer_ && device_claimer_->default_claimer()) { if (!device_claimer_->IsDeviceReleased(to_manage->link_name())) { Error error; device_claimer_->Claim(to_manage->link_name(), &error); return; } } for (const auto& device : devices_) { if (to_manage == device) return; } devices_.push_back(to_manage); LoadDeviceFromProfiles(to_manage); if (IsTechnologyProhibited(to_manage->technology())) { Error unused_error; to_manage->SetEnabledNonPersistent(false, &unused_error, ResultCallback()); } // If |to_manage| is new, it needs to be persisted. UpdateDevice(to_manage); // In normal usage, running_ will always be true when we are here, however // unit tests sometimes do things in otherwise invalid states. if (running_ && (to_manage->enabled_persistent() || to_manage->IsUnderlyingDeviceEnabled())) to_manage->SetEnabled(true); EmitDeviceProperties(); } void Manager::DeregisterDevice(const DeviceRefPtr& to_forget) { SLOG(this, 2) << __func__ << "(" << to_forget->FriendlyName() << ")"; for (auto it = devices_.begin(); it != devices_.end(); ++it) { if (to_forget.get() == it->get()) { SLOG(this, 2) << "Deregistered device: " << to_forget->UniqueName(); UpdateDevice(to_forget); to_forget->SetEnabled(false); devices_.erase(it); EmitDeviceProperties(); return; } } SLOG(this, 2) << __func__ << " unknown device: " << to_forget->UniqueName(); } void Manager::DeregisterDeviceByLinkName(const string& link_name) { for (const auto& device : devices_) { if (device->link_name() == link_name) { DeregisterDevice(device); break; } } } vector Manager::ClaimedDevices(Error* error) { vector results; if (!device_claimer_) { return results; } const auto& devices = device_claimer_->claimed_device_names(); results.resize(devices.size()); std::copy(devices.begin(), devices.end(), results.begin()); return results; } void Manager::LoadDeviceFromProfiles(const DeviceRefPtr& device) { // We are applying device properties from the DefaultProfile, and adding the // union of hidden services in all loaded profiles to the device. for (const auto& profile : profiles_) { // Load device configuration, if any exists, as well as hidden services. profile->ConfigureDevice(device); } } void Manager::EmitDeviceProperties() { Error error; vector device_paths = EnumerateDevices(&error); adaptor_->EmitRpcIdentifierArrayChanged(kDevicesProperty, device_paths); adaptor_->EmitStringsChanged(kAvailableTechnologiesProperty, AvailableTechnologies(&error)); adaptor_->EmitStringsChanged(kEnabledTechnologiesProperty, EnabledTechnologies(&error)); adaptor_->EmitStringsChanged(kUninitializedTechnologiesProperty, UninitializedTechnologies(&error)); } void Manager::OnInnerDevicesChanged() { EmitDeviceProperties(); } void Manager::OnDeviceClaimerVanished() { // Reset device claimer. device_claimer_.reset(); } #if !defined(DISABLE_WIFI) bool Manager::SetDisableWiFiVHT(const bool& disable_wifi_vht, Error* error) { if (disable_wifi_vht == wifi_provider_->disable_vht()) { return false; } wifi_provider_->set_disable_vht(disable_wifi_vht); return true; } bool Manager::GetDisableWiFiVHT(Error* error) { return wifi_provider_->disable_vht(); } #endif // DISABLE_WIFI bool Manager::SetProhibitedTechnologies(const string& prohibited_technologies, Error* error) { vector technology_vector; if (!Technology::GetTechnologyVectorFromString(prohibited_technologies, &technology_vector, error)) { return false; } for (const auto& technology : technology_vector) { Error unused_error(Error::kOperationInitiated); ResultCallback result_callback(Bind( &Manager::OnTechnologyProhibited, Unretained(this), technology)); const bool kPersistentSave = false; SetEnabledStateForTechnology(Technology::NameFromIdentifier(technology), false, kPersistentSave, &unused_error, result_callback); } props_.prohibited_technologies = prohibited_technologies; return true; } void Manager::OnTechnologyProhibited(Technology::Identifier technology, const Error& error) { SLOG(this, 2) << __func__ << " for " << Technology::NameFromIdentifier(technology); } string Manager::GetProhibitedTechnologies(Error* error) { return props_.prohibited_technologies; } bool Manager::HasService(const ServiceRefPtr& service) { for (const auto& manager_service : services_) { if (manager_service->unique_name() == service->unique_name()) return true; } return false; } void Manager::RegisterService(const ServiceRefPtr& to_manage) { SLOG(this, 2) << "Registering service " << to_manage->unique_name(); MatchProfileWithService(to_manage); // Now add to OUR list. for (const auto& service : services_) { CHECK(to_manage->unique_name() != service->unique_name()); } services_.push_back(to_manage); SortServices(); } void Manager::DeregisterService(const ServiceRefPtr& to_forget) { for (auto it = services_.begin(); it != services_.end(); ++it) { if (to_forget->unique_name() == (*it)->unique_name()) { DLOG_IF(FATAL, (*it)->connection()) << "Service " << (*it)->unique_name() << " still has a connection (in call to " << __func__ << ")"; (*it)->Unload(); (*it)->SetProfile(nullptr); services_.erase(it); SortServices(); return; } } } bool Manager::UnloadService(vector::iterator* service_iterator) { if (!(**service_iterator)->Unload()) { return false; } DCHECK(!(**service_iterator)->connection()); (**service_iterator)->SetProfile(nullptr); *service_iterator = services_.erase(*service_iterator); return true; } void Manager::UpdateService(const ServiceRefPtr& to_update) { CHECK(to_update); bool is_interesting_state_change = false; const auto& state_it = watched_service_states_.find(to_update->unique_name()); if (state_it != watched_service_states_.end()) { is_interesting_state_change = (to_update->state() != state_it->second); } else { is_interesting_state_change = to_update->IsActive(nullptr); } string log_message = StringPrintf( "Service %s updated; state: %s failure %s", to_update->unique_name().c_str(), Service::ConnectStateToString(to_update->state()), Service::ConnectFailureToString(to_update->failure())); if (is_interesting_state_change) { LOG(INFO) << log_message; } else { SLOG(this, 2) << log_message; } SLOG(this, 2) << "IsConnected(): " << to_update->IsConnected(); SLOG(this, 2) << "IsConnecting(): " << to_update->IsConnecting(); if (to_update->IsConnected()) { to_update->EnableAndRetainAutoConnect(); // Persists the updated auto_connect setting in the profile. SaveServiceToProfile(to_update); } SortServices(); } void Manager::UpdateDevice(const DeviceRefPtr& to_update) { LOG(INFO) << "Device " << to_update->link_name() << " updated: " << (to_update->enabled_persistent() ? "enabled" : "disabled"); // Saves the device to the topmost profile that accepts it (ordinary // profiles don't update but default profiles do). Normally, the topmost // updating profile would be the DefaultProfile at the bottom of the stack. // Autotests, differ from the normal scenario, however, in that they push a // second test-only DefaultProfile. for (auto rit = profiles_.rbegin(); rit != profiles_.rend(); ++rit) { if ((*rit)->UpdateDevice(to_update)) { return; } } } #if !defined(DISABLE_WIFI) void Manager::UpdateWiFiProvider() { // Saves |wifi_provider_| to the topmost profile that accepts it (ordinary // profiles don't update but default profiles do). Normally, the topmost // updating profile would be the DefaultProfile at the bottom of the stack. // Autotests, differ from the normal scenario, however, in that they push a // second test-only DefaultProfile. for (auto rit = profiles_.rbegin(); rit != profiles_.rend(); ++rit) { if ((*rit)->UpdateWiFiProvider(*wifi_provider_)) { return; } } } #endif // DISABLE_WIFI void Manager::SaveServiceToProfile(const ServiceRefPtr& to_update) { if (IsServiceEphemeral(to_update)) { if (profiles_.empty()) { LOG(ERROR) << "Cannot assign profile to service: no profiles exist!"; } else { MoveServiceToProfile(to_update, profiles_.back()); } } else { to_update->profile()->UpdateService(to_update); } } void Manager::LoadProperties(const scoped_refptr& profile) { profile->LoadManagerProperties(&props_, dhcp_properties_.get()); SetIgnoredDNSSearchPaths(props_.ignored_dns_search_paths, nullptr); } void Manager::AddTerminationAction(const string& name, const base::Closure& start) { termination_actions_.Add(name, start); } void Manager::TerminationActionComplete(const string& name) { SLOG(this, 2) << __func__; termination_actions_.ActionComplete(name); } void Manager::RemoveTerminationAction(const string& name) { SLOG(this, 2) << __func__; termination_actions_.Remove(name); } void Manager::RunTerminationActions(const ResultCallback& done_callback) { LOG(INFO) << "Running termination actions."; termination_actions_.Run(kTerminationActionsTimeoutMilliseconds, done_callback); } bool Manager::RunTerminationActionsAndNotifyMetrics( const ResultCallback& done_callback) { if (termination_actions_.IsEmpty()) return false; metrics_->NotifyTerminationActionsStarted(); RunTerminationActions(done_callback); return true; } int Manager::RegisterDefaultServiceCallback(const ServiceCallback& callback) { default_service_callbacks_[++default_service_callback_tag_] = callback; return default_service_callback_tag_; } void Manager::DeregisterDefaultServiceCallback(int tag) { default_service_callbacks_.erase(tag); } #if !defined(DISABLE_WIFI) void Manager::VerifyDestination(const string& certificate, const string& public_key, const string& nonce, const string& signed_data, const string& destination_udn, const string& hotspot_ssid, const string& hotspot_bssid, const ResultBoolCallback& cb, Error* error) { if (hotspot_bssid.length() > 32) { error->Populate(Error::kOperationFailed, "Invalid SSID given for verification."); return; } vector ssid; string bssid; if (hotspot_ssid.length() || hotspot_bssid.length()) { // If Chrome thinks this destination is already configured, service // will be an AP that both we and the destination are connected // to, and not the thing we should verify against. ssid.assign(hotspot_ssid.begin(), hotspot_ssid.end()); bssid = hotspot_bssid; } else { // For now, we only support a single connected WiFi service. If we change // that, we'll need to revisit this. bool found_one = false; for (const auto& service : services_) { if (service->technology() == Technology::kWifi && service->IsConnected()) { WiFiService* wifi = reinterpret_cast(&(*service)); bssid = wifi->bssid(); ssid = wifi->ssid(); found_one = true; break; } } if (!found_one) { error->Populate(Error::kOperationFailed, "Unable to find connected WiFi service."); return; } } crypto_util_proxy_->VerifyDestination(certificate, public_key, nonce, signed_data, destination_udn, ssid, bssid, cb, error); } void Manager::VerifyToEncryptLink(string public_key, string data, ResultStringCallback cb, const Error& error, bool success) { if (!success || !error.IsSuccess()) { CHECK(error.IsFailure()) << "Return code from CryptoUtilProxy " << "inconsistent with error code."; cb.Run(error, ""); return; } Error encrypt_error; if (!crypto_util_proxy_->EncryptData(public_key, data, cb, &encrypt_error)) { CHECK(encrypt_error.IsFailure()) << "CryptoUtilProxy::EncryptData returned " << "inconsistently."; cb.Run(encrypt_error, ""); } } void Manager::VerifyAndEncryptData(const string& certificate, const string& public_key, const string& nonce, const string& signed_data, const string& destination_udn, const string& hotspot_ssid, const string& hotspot_bssid, const string& data, const ResultStringCallback& cb, Error* error) { ResultBoolCallback on_verification_success = Bind( &Manager::VerifyToEncryptLink, AsWeakPtr(), public_key, data, cb); VerifyDestination(certificate, public_key, nonce, signed_data, destination_udn, hotspot_ssid, hotspot_bssid, on_verification_success, error); } void Manager::VerifyAndEncryptCredentials(const string& certificate, const string& public_key, const string& nonce, const string& signed_data, const string& destination_udn, const string& hotspot_ssid, const string& hotspot_bssid, const string& network_path, const ResultStringCallback& cb, Error* error) { // This is intentionally left unimplemented until we have a security review. error->Populate(Error::kNotImplemented, "Not implemented"); } #endif // DISABLE_WIFI int Manager::CalcConnectionId(std::string gateway_ip, std::string gateway_mac) { return static_cast(std::hash()(gateway_ip + gateway_mac + std::to_string(props_.connection_id_salt))); } void Manager::ReportServicesOnSameNetwork(int connection_id) { int num_services = 0; for (const auto& service : services_) { if (service->connection_id() == connection_id) { num_services++; } } metrics_->NotifyServicesOnSameNetwork(num_services); } void Manager::NotifyDefaultServiceChanged(const ServiceRefPtr& service) { for (const auto& callback : default_service_callbacks_) { callback.second.Run(service); } metrics_->NotifyDefaultServiceChanged(service.get()); EmitDefaultService(); } void Manager::EmitDefaultService() { RpcIdentifier rpc_identifier = GetDefaultServiceRpcIdentifier(nullptr); if (rpc_identifier != default_service_rpc_identifier_) { adaptor_->EmitRpcIdentifierChanged(kDefaultServiceProperty, rpc_identifier); default_service_rpc_identifier_ = rpc_identifier; } } void Manager::OnSuspendImminent() { metrics_->NotifySuspendActionsStarted(); if (devices_.empty()) { // If there are no devices, then suspend actions succeeded synchronously. // Make a call to the Manager::OnSuspendActionsComplete directly, since // result_aggregator will not. OnSuspendActionsComplete(Error(Error::kSuccess)); return; } auto result_aggregator(make_scoped_refptr(new ResultAggregator( Bind(&Manager::OnSuspendActionsComplete, AsWeakPtr()), dispatcher_, kTerminationActionsTimeoutMilliseconds))); for (const auto& device : devices_) { ResultCallback aggregator_callback( Bind(&ResultAggregator::ReportResult, result_aggregator)); device->OnBeforeSuspend(aggregator_callback); } } void Manager::OnSuspendDone() { metrics_->NotifySuspendDone(); // Un-suppress auto-connect in case this flag was left set in dark resume. set_suppress_autoconnect(false); for (const auto& service : services_) { service->OnAfterResume(); } SortServices(); for (const auto& device : devices_) { device->OnAfterResume(); } } void Manager::OnDarkSuspendImminent() { metrics_->NotifyDarkResumeActionsStarted(); if (devices_.empty()) { // If there are no devices, then suspend actions succeeded synchronously. // Make a call to the Manager::OnDarkResumeActionsComplete directly, since // result_aggregator will not. OnDarkResumeActionsComplete(Error(Error::kSuccess)); return; } auto result_aggregator(make_scoped_refptr(new ResultAggregator( Bind(&Manager::OnDarkResumeActionsComplete, AsWeakPtr()), dispatcher_, kTerminationActionsTimeoutMilliseconds))); for (const auto& device : devices_) { ResultCallback aggregator_callback( Bind(&ResultAggregator::ReportResult, result_aggregator)); device->OnDarkResume(aggregator_callback); } } void Manager::OnSuspendActionsComplete(const Error& error) { LOG(INFO) << "Finished suspend actions. Result: " << error; metrics_->NotifySuspendActionsCompleted(error.IsSuccess()); power_manager_->ReportSuspendReadiness(); } void Manager::OnDarkResumeActionsComplete(const Error& error) { LOG(INFO) << "Finished dark resume actions. Result: " << error; metrics_->NotifyDarkResumeActionsCompleted(error.IsSuccess()); power_manager_->ReportDarkSuspendReadiness(); } vector Manager::FilterByTechnology(Technology::Identifier tech) const { vector found; for (const auto& device : devices_) { if (device->technology() == tech) found.push_back(device); } return found; } ServiceRefPtr Manager::FindService(const string& name) { for (const auto& service : services_) { if (name == service->unique_name()) return service; } return nullptr; } void Manager::HelpRegisterConstDerivedRpcIdentifier( const string& name, RpcIdentifier(Manager::*get)(Error* error)) { store_.RegisterDerivedRpcIdentifier( name, RpcIdentifierAccessor( new CustomAccessor(this, get, nullptr))); } void Manager::HelpRegisterConstDerivedRpcIdentifiers( const string& name, RpcIdentifiers(Manager::*get)(Error* error)) { store_.RegisterDerivedRpcIdentifiers( name, RpcIdentifiersAccessor( new CustomAccessor(this, get, nullptr))); } void Manager::HelpRegisterDerivedString( const string& name, string(Manager::*get)(Error* error), bool(Manager::*set)(const string&, Error*)) { store_.RegisterDerivedString( name, StringAccessor(new CustomAccessor(this, get, set))); } void Manager::HelpRegisterConstDerivedStrings( const string& name, Strings(Manager::*get)(Error*)) { store_.RegisterDerivedStrings( name, StringsAccessor( new CustomAccessor(this, get, nullptr))); } void Manager::HelpRegisterDerivedBool( const string& name, bool(Manager::*get)(Error* error), bool(Manager::*set)(const bool&, Error*)) { store_.RegisterDerivedBool( name, BoolAccessor(new CustomAccessor(this, get, set, nullptr))); } void Manager::SortServices() { // We might be called in the middle of a series of events that // may result in multiple calls to Manager::SortServices, or within // an outer loop that may also be traversing the services_ list. // Defer this work to the event loop. if (sort_services_task_.IsCancelled()) { sort_services_task_.Reset(Bind(&Manager::SortServicesTask, AsWeakPtr())); dispatcher_->PostTask(sort_services_task_.callback()); } } void Manager::SortServicesTask() { SLOG(this, 4) << "In " << __func__; sort_services_task_.Cancel(); ServiceRefPtr default_service; if (!services_.empty()) { // Keep track of the service that is the candidate for the default // service. We have not yet tested to see if this service has a // connection. default_service = services_[0]; } const bool kCompareConnectivityState = true; sort(services_.begin(), services_.end(), ServiceSorter(this, kCompareConnectivityState, technology_order_)); if (!services_.empty()) { ConnectionRefPtr default_connection = default_service->connection(); if (default_connection && services_[0]->connection() != default_connection) { default_connection->SetIsDefault(false); } if (services_[0]->connection()) { services_[0]->connection()->SetIsDefault(true); if (default_service != services_[0]) { default_service = services_[0]; LOG(INFO) << "Default service is now " << default_service->unique_name(); } } else { default_service = nullptr; } } Error error; adaptor_->EmitRpcIdentifierArrayChanged(kServiceCompleteListProperty, EnumerateCompleteServices(nullptr)); adaptor_->EmitRpcIdentifierArrayChanged(kServicesProperty, EnumerateAvailableServices(nullptr)); adaptor_->EmitRpcIdentifierArrayChanged(kServiceWatchListProperty, EnumerateWatchedServices(nullptr)); adaptor_->EmitStringsChanged(kConnectedTechnologiesProperty, ConnectedTechnologies(&error)); adaptor_->EmitStringChanged(kDefaultTechnologyProperty, DefaultTechnology(&error)); NotifyDefaultServiceChanged(default_service); RefreshConnectionState(); DetectMultiHomedDevices(); AutoConnect(); } void Manager::DeviceStatusCheckTask() { SLOG(this, 4) << "In " << __func__; ConnectionStatusCheck(); DevicePresenceStatusCheck(); dispatcher_->PostDelayedTask(device_status_check_task_.callback(), kDeviceStatusCheckIntervalMilliseconds); } void Manager::ConnectionStatusCheck() { SLOG(this, 4) << "In " << __func__; // Report current connection status. Metrics::ConnectionStatus status = Metrics::kConnectionStatusOffline; if (IsConnected()) { status = Metrics::kConnectionStatusConnected; // Check if device is online as well. if (IsOnline()) { metrics_->NotifyDeviceConnectionStatus(Metrics::kConnectionStatusOnline); } } metrics_->NotifyDeviceConnectionStatus(status); } void Manager::DevicePresenceStatusCheck() { Error error; vector available_technologies = AvailableTechnologies(&error); for (const auto& technology : kProbeTechnologies) { bool presence = std::find(available_technologies.begin(), available_technologies.end(), technology) != available_technologies.end(); metrics_->NotifyDevicePresenceStatus( Technology::IdentifierFromName(technology), presence); } } bool Manager::MatchProfileWithService(const ServiceRefPtr& service) { vector::reverse_iterator it; for (it = profiles_.rbegin(); it != profiles_.rend(); ++it) { if ((*it)->ConfigureService(service)) { break; } } if (it == profiles_.rend()) { ephemeral_profile_->AdoptService(service); return false; } return true; } void Manager::AutoConnect() { if (suppress_autoconnect_) { LOG(INFO) << "Auto-connect suppressed -- explicitly suppressed."; return; } if (!running_) { LOG(INFO) << "Auto-connect suppressed -- not running."; return; } if (power_manager_ && power_manager_->suspending() && !power_manager_->in_dark_resume()) { LOG(INFO) << "Auto-connect suppressed -- system is suspending."; return; } if (services_.empty()) { LOG(INFO) << "Auto-connect suppressed -- no services."; return; } if (SLOG_IS_ON(Manager, 4)) { SLOG(this, 4) << "Sorted service list for AutoConnect: "; for (size_t i = 0; i < services_.size(); ++i) { ServiceRefPtr service = services_[i]; const char* compare_reason = nullptr; if (i + 1 < services_.size()) { const bool kCompareConnectivityState = true; Service::Compare( this, service, services_[i+1], kCompareConnectivityState, technology_order_, &compare_reason); } else { compare_reason = "last"; } SLOG(this, 4) << "Service " << service->unique_name() << " Profile: " << service->profile()->GetFriendlyName() << " IsConnected: " << service->IsConnected() << " IsConnecting: " << service->IsConnecting() << " HasEverConnected: " << service->has_ever_connected() << " IsFailed: " << service->IsFailed() << " connectable: " << service->connectable() << " auto_connect: " << service->auto_connect() << " retain_auto_connect: " << service->retain_auto_connect() << " priority: " << service->priority() << " crypto_algorithm: " << service->crypto_algorithm() << " key_rotation: " << service->key_rotation() << " endpoint_auth: " << service->endpoint_auth() << " strength: " << service->strength() << " sorted: " << compare_reason; } } #if !defined(DISABLE_WIFI) // Report the number of auto-connectable wifi services available when wifi is // idle (no active or pending connection), which will trigger auto connect // for wifi services. if (IsWifiIdle()) { wifi_provider_->ReportAutoConnectableServices(); } #endif // DISABLE_WIFI // Perform auto-connect. for (const auto& service : services_) { if (service->auto_connect()) { service->AutoConnect(); } } } void Manager::ConnectToBestServices(Error* /*error*/) { dispatcher_->PostTask(Bind(&Manager::ConnectToBestServicesTask, AsWeakPtr())); } void Manager::ConnectToBestServicesTask() { vector services_copy = services_; const bool kCompareConnectivityState = false; sort(services_copy.begin(), services_copy.end(), ServiceSorter(this, kCompareConnectivityState, technology_order_)); set connecting_technologies; for (const auto& service : services_copy) { if (!service->connectable()) { // Due to service sort order, it is guaranteed that no services beyond // this one will be connectable either. break; } if (!service->auto_connect() || !service->IsVisible()) { continue; } Technology::Identifier technology = service->technology(); if (!Technology::IsPrimaryConnectivityTechnology(technology) && !IsConnected()) { // Non-primary services need some other service connected first. continue; } if (ContainsKey(connecting_technologies, technology)) { // We have already started a connection for this technology. continue; } if (service->explicitly_disconnected()) continue; connecting_technologies.insert(technology); if (!service->IsConnected() && !service->IsConnecting()) { // At first blush, it may seem that using Service::AutoConnect might // be the right choice, however Service::IsAutoConnectable and its // overridden implementations consider a host of conditions which // prevent it from attempting a connection which we'd like to ignore // for the purposes of this user-initiated action. Error error; service->Connect(&error, __func__); if (error.IsFailure()) { LOG(ERROR) << "Connection failed: " << error.message(); } } } if (SLOG_IS_ON(Manager, 4)) { SLOG(this, 4) << "Sorted service list for ConnectToBestServicesTask: "; for (size_t i = 0; i < services_copy.size(); ++i) { ServiceRefPtr service = services_copy[i]; const char* compare_reason = nullptr; if (i + 1 < services_copy.size()) { if (!service->connectable()) { // Due to service sort order, it is guaranteed that no services beyond // this one are connectable either. break; } Service::Compare( this, service, services_copy[i+1], kCompareConnectivityState, technology_order_, &compare_reason); } else { compare_reason = "last"; } SLOG(this, 4) << "Service " << service->unique_name() << " Profile: " << service->profile()->GetFriendlyName() << " IsConnected: " << service->IsConnected() << " IsConnecting: " << service->IsConnecting() << " HasEverConnected: " << service->has_ever_connected() << " IsFailed: " << service->IsFailed() << " connectable: " << service->connectable() << " auto_connect: " << service->auto_connect() << " retain_auto_connect: " << service->retain_auto_connect() << " priority: " << service->priority() << " crypto_algorithm: " << service->crypto_algorithm() << " key_rotation: " << service->key_rotation() << " endpoint_auth: " << service->endpoint_auth() << " strength: " << service->strength() << " sorted: " << compare_reason; } } } void Manager::CreateConnectivityReport(Error* /*error*/) { LOG(INFO) << "Creating Connectivity Report"; // For each of the connected services, perform a single portal detection // test to assess connectivity. The results should be written to the log. for (const auto& service : services_) { if (!service->IsConnected()) { // Service sort order guarantees that no service beyond this one will be // connected either. break; } // Get the underlying device for this service and perform connectivity test. for (const auto& device : devices_) { if (device->IsConnectedToService(service)) { if (device->StartConnectivityTest()) { SLOG(this, 3) << "Started connectivity test for service " << service->unique_name(); } else { SLOG(this, 3) << "Failed to start connectivity test for service " << service->unique_name() << " device not reporting IsConnected."; } break; } } } } bool Manager::IsConnected() const { // |services_| is sorted such that connected services are first. return !services_.empty() && services_.front()->IsConnected(); } bool Manager::IsOnline() const { // |services_| is sorted such that online services are first. return !services_.empty() && services_.front()->IsOnline(); } string Manager::CalculateState(Error* /*error*/) { return IsConnected() ? kStateOnline : kStateOffline; } void Manager::RefreshConnectionState() { const ServiceRefPtr& service = GetDefaultService(); string connection_state = service ? service->GetStateString() : kStateIdle; if (connection_state_ == connection_state) { return; } connection_state_ = connection_state; adaptor_->EmitStringChanged(kConnectionStateProperty, connection_state_); // Send upstart notifications for the initial idle state // and when we transition in/out of connected states. if ((!is_connected_state_) && (IsConnected())) { is_connected_state_ = true; upstart_->NotifyConnected(); } else if ((is_connected_state_) && (!IsConnected())) { is_connected_state_ = false; upstart_->NotifyDisconnected(); } else if (connection_state_ == kStateIdle) { upstart_->NotifyDisconnected(); } } vector Manager::AvailableTechnologies(Error* /*error*/) { set unique_technologies; for (const auto& device : devices_) { unique_technologies.insert( Technology::NameFromIdentifier(device->technology())); } return vector(unique_technologies.begin(), unique_technologies.end()); } vector Manager::ConnectedTechnologies(Error* /*error*/) { set unique_technologies; for (const auto& device : devices_) { if (device->IsConnected()) unique_technologies.insert( Technology::NameFromIdentifier(device->technology())); } return vector(unique_technologies.begin(), unique_technologies.end()); } bool Manager::IsTechnologyConnected(Technology::Identifier technology) const { for (const auto& device : devices_) { if (device->technology() == technology && device->IsConnected()) return true; } return false; } string Manager::DefaultTechnology(Error* /*error*/) { return (!services_.empty() && services_[0]->IsConnected()) ? services_[0]->GetTechnologyString() : ""; } vector Manager::EnabledTechnologies(Error* /*error*/) { set unique_technologies; for (const auto& device : devices_) { if (device->enabled()) unique_technologies.insert( Technology::NameFromIdentifier(device->technology())); } return vector(unique_technologies.begin(), unique_technologies.end()); } vector Manager::UninitializedTechnologies(Error* /*error*/) { return device_info_.GetUninitializedTechnologies(); } RpcIdentifiers Manager::EnumerateDevices(Error* /*error*/) { RpcIdentifiers device_rpc_ids; for (const auto& device : devices_) { device_rpc_ids.push_back(device->GetRpcIdentifier()); } // Enumerate devices that are internal to the services, such as PPPoE devices. for (const auto& service : services_) { if (!service->GetInnerDeviceRpcIdentifier().empty()) { device_rpc_ids.push_back(service->GetInnerDeviceRpcIdentifier()); } } return device_rpc_ids; } RpcIdentifiers Manager::EnumerateProfiles(Error* /*error*/) { RpcIdentifiers profile_rpc_ids; for (const auto& profile : profiles_) { profile_rpc_ids.push_back(profile->GetRpcIdentifier()); } return profile_rpc_ids; } RpcIdentifiers Manager::EnumerateAvailableServices(Error* /*error*/) { RpcIdentifiers service_rpc_ids; for (const auto& service : services_) { if (service->IsVisible()) { service_rpc_ids.push_back(service->GetRpcIdentifier()); } } return service_rpc_ids; } RpcIdentifiers Manager::EnumerateCompleteServices(Error* /*error*/) { RpcIdentifiers service_rpc_ids; for (const auto& service : services_) { service_rpc_ids.push_back(service->GetRpcIdentifier()); } return service_rpc_ids; } RpcIdentifiers Manager::EnumerateWatchedServices(Error* /*error*/) { RpcIdentifiers service_rpc_ids; watched_service_states_.clear(); for (const auto& service : services_) { if (service->IsVisible() && service->IsActive(nullptr)) { service_rpc_ids.push_back(service->GetRpcIdentifier()); watched_service_states_[service->unique_name()] = service->state(); } } return service_rpc_ids; } string Manager::GetActiveProfileRpcIdentifier(Error* /*error*/) { return ActiveProfile()->GetRpcIdentifier(); } string Manager::GetCheckPortalList(Error* /*error*/) { return use_startup_portal_list_ ? startup_portal_list_ : props_.check_portal_list; } bool Manager::SetCheckPortalList(const string& portal_list, Error* error) { use_startup_portal_list_ = false; if (props_.check_portal_list == portal_list) { return false; } props_.check_portal_list = portal_list; return true; } string Manager::GetIgnoredDNSSearchPaths(Error* /*error*/) { return props_.ignored_dns_search_paths; } bool Manager::SetIgnoredDNSSearchPaths(const string& ignored_paths, Error* /*error*/) { if (props_.ignored_dns_search_paths == ignored_paths) { return false; } vector ignored_path_list; if (!ignored_paths.empty()) { ignored_path_list = base::SplitString( ignored_paths, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); } props_.ignored_dns_search_paths = ignored_paths; resolver_->set_ignored_search_list(ignored_path_list); return true; } // called via RPC (e.g., from ManagerDBusAdaptor) ServiceRefPtr Manager::GetService(const KeyValueStore& args, Error* error) { if (args.ContainsString(kTypeProperty) && args.GetString(kTypeProperty) == kTypeVPN) { // GetService on a VPN service should actually perform ConfigureService. // TODO(pstew): Remove this hack and change Chrome to use ConfigureService // instead, when we no longer need to support flimflam. crbug.com/213802 return ConfigureService(args, error); } ServiceRefPtr service = GetServiceInner(args, error); if (service) { // Configures the service using the rest of the passed-in arguments. service->Configure(args, error); } return service; } ServiceRefPtr Manager::GetServiceInner(const KeyValueStore& args, Error* error) { if (args.ContainsString(kGuidProperty)) { SLOG(this, 2) << __func__ << ": searching by GUID"; ServiceRefPtr service = GetServiceWithGUID(args.GetString(kGuidProperty), nullptr); if (service) { return service; } } if (!args.ContainsString(kTypeProperty)) { Error::PopulateAndLog( FROM_HERE, error, Error::kInvalidArguments, kErrorTypeRequired); return nullptr; } string type = args.GetString(kTypeProperty); Technology::Identifier technology = Technology::IdentifierFromName(type); if (!ContainsKey(providers_, technology)) { Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported, kErrorUnsupportedServiceType); return nullptr; } SLOG(this, 2) << __func__ << ": getting " << type << " Service"; return providers_[technology]->GetService(args, error); } // called via RPC (e.g., from ManagerDBusAdaptor) ServiceRefPtr Manager::ConfigureService(const KeyValueStore& args, Error* error) { ProfileRefPtr profile = ActiveProfile(); bool profile_specified = args.ContainsString(kProfileProperty); if (profile_specified) { string profile_rpcid = args.GetString(kProfileProperty); profile = LookupProfileByRpcIdentifier(profile_rpcid); if (!profile) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Invalid profile name " + profile_rpcid); return nullptr; } } ServiceRefPtr service = GetServiceInner(args, error); if (error->IsFailure() || !service) { LOG(ERROR) << "GetService failed; returning upstream error."; return nullptr; } // First pull in any stored configuration associated with the service. if (service->profile() == profile) { SLOG(this, 2) << __func__ << ": service " << service->unique_name() << " is already a member of profile " << profile->GetFriendlyName() << " so a load is not necessary."; } else if (profile->LoadService(service)) { SLOG(this, 2) << __func__ << ": applied stored information from profile " << profile->GetFriendlyName() << " into service " << service->unique_name(); } else { SLOG(this, 2) << __func__ << ": no previous information in profile " << profile->GetFriendlyName() << " exists for service " << service->unique_name(); } // Overlay this with the passed-in configuration parameters. service->Configure(args, error); // Overwrite the profile data with the resulting configured service. if (!profile->UpdateService(service)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInternalError, "Unable to save service to profile"); return nullptr; } if (HasService(service)) { // If the service has been registered (it may not be -- as is the case // with invisible WiFi networks), we can now transfer the service between // profiles. if (IsServiceEphemeral(service) || (profile_specified && service->profile() != profile)) { SLOG(this, 2) << "Moving service to profile " << profile->GetFriendlyName(); if (!MoveServiceToProfile(service, profile)) { Error::PopulateAndLog(FROM_HERE, error, Error::kInternalError, "Unable to move service to profile"); } } } // Notify the service that a profile has been configured for it. service->OnProfileConfigured(); return service; } // called via RPC (e.g., from ManagerDBusAdaptor) ServiceRefPtr Manager::ConfigureServiceForProfile( const string& profile_rpcid, const KeyValueStore& args, Error* error) { if (!args.ContainsString(kTypeProperty)) { Error::PopulateAndLog( FROM_HERE, error, Error::kInvalidArguments, kErrorTypeRequired); return nullptr; } string type = args.GetString(kTypeProperty); Technology::Identifier technology = Technology::IdentifierFromName(type); if (!ContainsKey(providers_, technology)) { Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported, kErrorUnsupportedServiceType); return nullptr; } ProviderInterface* provider = providers_[technology]; ProfileRefPtr profile = LookupProfileByRpcIdentifier(profile_rpcid); if (!profile) { Error::PopulateAndLog(FROM_HERE, error, Error::kNotFound, "Profile specified was not found"); return nullptr; } if (args.LookupString(kProfileProperty, profile_rpcid) != profile_rpcid) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Profile argument does not match that in " "the configuration arguments"); return nullptr; } ServiceRefPtr service; if (args.ContainsString(kGuidProperty)) { SLOG(this, 2) << __func__ << ": searching by GUID"; service = GetServiceWithGUID(args.GetString(kGuidProperty), nullptr); if (service && service->technology() != technology) { Error::PopulateAndLog(FROM_HERE, error, Error::kNotSupported, StringPrintf("This GUID matches a non-%s service", type.c_str())); return nullptr; } } if (!service) { Error find_error; service = provider->FindSimilarService(args, &find_error); } // If no matching service exists, create a new service in the specified // profile using ConfigureService(). if (!service) { KeyValueStore configure_args; configure_args.CopyFrom(args); configure_args.SetString(kProfileProperty, profile_rpcid); return ConfigureService(configure_args, error); } // The service already exists and is set to the desired profile, // the service is in the ephemeral profile, or the current profile // for the service appears before the desired profile, we need to // reassign the service to the new profile if necessary, leaving // the old profile intact (i.e, not calling Profile::AbandonService()). // Then, configure the properties on the service as well as its newly // associated profile. if (service->profile() == profile || IsServiceEphemeral(service) || IsProfileBefore(service->profile(), profile)) { SetupServiceInProfile(service, profile, args, error); return service; } // The current profile for the service appears after the desired // profile. We must create a temporary service specifically for // the task of creating configuration data. This service will // neither inherit properties from the visible service, nor will // it exist after this function returns. service = provider->CreateTemporaryService(args, error); if (!service || !error->IsSuccess()) { // Service::CreateTemporaryService() failed, and has set the error // appropriately. return nullptr; } // The profile may already have configuration for this service. profile->ConfigureService(service); SetupServiceInProfile(service, profile, args, error); // Although we have succeeded, this service will not exist, so its // path is of no use to the caller. DCHECK(service->HasOneRef()); return nullptr; } void Manager::SetupServiceInProfile(ServiceRefPtr service, ProfileRefPtr profile, const KeyValueStore& args, Error* error) { service->SetProfile(profile); service->Configure(args, error); profile->UpdateService(service); } ServiceRefPtr Manager::FindMatchingService(const KeyValueStore& args, Error* error) { for (const auto& service : services_) { if (service->DoPropertiesMatch(args)) { return service; } } error->Populate(Error::kNotFound, "Matching service was not found"); return nullptr; } const map &Manager::GetNetworksForGeolocation() const { return networks_for_geolocation_; } void Manager::OnDeviceGeolocationInfoUpdated(const DeviceRefPtr& device) { SLOG(this, 2) << __func__ << " for technology " << Technology::NameFromIdentifier(device->technology()); switch (device->technology()) { // TODO(gauravsh): crbug.com/217833 Need a strategy for combining // geolocation objects from multiple devices of the same technolgy. // Currently, we just override the any previously acquired // geolocation objects for the retrieved technology type. case Technology::kWifi: networks_for_geolocation_[kGeoWifiAccessPointsProperty] = device->GetGeolocationObjects(); break; case Technology::kCellular: networks_for_geolocation_[kGeoCellTowersProperty] = device->GetGeolocationObjects(); break; default: // Ignore other technologies. break; } } void Manager::RecheckPortal(Error* /*error*/) { for (const auto& device : devices_) { if (device->RequestPortalDetection()) { // Only start Portal Detection on the device with the default connection. // We will get a "true" return value when we've found that device, and // can end our loop early as a result. break; } } } void Manager::RecheckPortalOnService(const ServiceRefPtr& service) { for (const auto& device : devices_) { if (device->IsConnectedToService(service)) { // As opposed to RecheckPortal() above, we explicitly stop and then // restart portal detection, since the service to recheck was explicitly // specified. device->RestartPortalDetection(); break; } } } void Manager::RequestScan(Device::ScanType scan_type, const string& technology, Error* error) { if (technology == kTypeWifi || technology == "") { for (const auto& wifi_device : FilterByTechnology(Technology::kWifi)) { metrics_->NotifyUserInitiatedEvent(Metrics::kUserInitiatedEventWifiScan); wifi_device->Scan(scan_type, error, __func__); } } else { // TODO(quiche): support scanning for other technologies? Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Unrecognized technology " + technology); } } void Manager::SetSchedScan(bool enable, Error* error) { for (const auto& wifi_device : FilterByTechnology(Technology::kWifi)) { wifi_device->SetSchedScan(enable, error); } } string Manager::GetTechnologyOrder() { vector technology_names; for (const auto& technology : technology_order_) { technology_names.push_back(Technology::NameFromIdentifier(technology)); } return base::JoinString(technology_names, ","); } void Manager::SetTechnologyOrder(const string& order, Error* error) { vector new_order; SLOG(this, 2) << "Setting technology order to " << order; if (!Technology::GetTechnologyVectorFromString(order, &new_order, error)) { return; } technology_order_ = new_order; if (running_) { SortServices(); } } bool Manager::IsWifiIdle() { bool ret = false; // Since services are sorted by connection state, status of the wifi device // can be determine by examing the connection state of the first wifi service. for (const auto& service : services_) { if (service->technology() == Technology::kWifi) { if (!service->IsConnecting() && !service->IsConnected()) { ret = true; } break; } } return ret; } void Manager::UpdateProviderMapping() { #if !defined(DISABLE_WIRED_8021X) providers_[Technology::kEthernetEap] = ethernet_eap_provider_.get(); #endif // DISABLE_WIRED_8021X providers_[Technology::kVPN] = vpn_provider_.get(); #if !defined(DISABLE_WIFI) providers_[Technology::kWifi] = wifi_provider_.get(); #endif // DISABLE_WIFI #if !defined(DISABLE_WIMAX) providers_[Technology::kWiMax] = wimax_provider_.get(); #endif // DISABLE_WIMAX } DeviceRefPtr Manager::GetDeviceConnectedToService(ServiceRefPtr service) { for (DeviceRefPtr device : devices_) { if (device->IsConnectedToService(service)) { return device; } } return nullptr; } void Manager::DetectMultiHomedDevices() { map> subnet_buckets; for (const auto& device : devices_) { const auto& connection = device->connection(); string subnet_name; if (connection) { subnet_name = connection->GetSubnetName(); } if (subnet_name.empty()) { device->SetIsMultiHomed(false); } else { subnet_buckets[subnet_name].push_back(device); } } for (const auto& subnet_bucket : subnet_buckets) { const auto& device_list = subnet_bucket.second; if (device_list.size() > 1) { for (const auto& device : device_list) { device->SetIsMultiHomed(true); } } else { DCHECK_EQ(1U, device_list.size()); device_list.back()->SetIsMultiHomed(false); } } } } // namespace shill