// // 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/wimax/wimax_provider.h" #include <algorithm> #include <set> #include <base/bind.h> #include <base/strings/string_util.h> #if defined(__ANDROID__) #include <dbus/service_constants.h> #else #include <chromeos/dbus/service_constants.h> #endif // __ANDROID__ #include "shill/control_interface.h" #include "shill/error.h" #include "shill/key_value_store.h" #include "shill/logging.h" #include "shill/manager.h" #include "shill/profile.h" #include "shill/store_interface.h" #include "shill/wimax/wimax.h" #include "shill/wimax/wimax_manager_proxy_interface.h" #include "shill/wimax/wimax_service.h" using base::Bind; using base::Unretained; using std::find; using std::map; using std::set; using std::string; namespace shill { namespace Logging { static auto kModuleLogScope = ScopeLogger::kWiMax; static string ObjectID(const WiMaxProvider* w) { return "(wimax_provider)"; } } WiMaxProvider::WiMaxProvider(ControlInterface* control, EventDispatcher* dispatcher, Metrics* metrics, Manager* manager) : control_(control), dispatcher_(dispatcher), metrics_(metrics), manager_(manager) {} WiMaxProvider::~WiMaxProvider() {} void WiMaxProvider::Start() { SLOG(this, 2) << __func__; // Create a proxy for WiMaxManager service. This provider will connect to it // if/when the OnWiMaxManagerAppear callback is invoked. wimax_manager_proxy_.reset( control_->CreateWiMaxManagerProxy( Bind(&WiMaxProvider::OnWiMaxManagerAppeared, Unretained(this)), Bind(&WiMaxProvider::OnWiMaxManagerVanished, Unretained(this)))); wimax_manager_proxy_->set_devices_changed_callback( Bind(&WiMaxProvider::OnDevicesChanged, Unretained(this))); } void WiMaxProvider::Stop() { SLOG(this, 2) << __func__; wimax_manager_proxy_.reset(); DisconnectFromWiMaxManager(); DestroyAllServices(); } void WiMaxProvider::ConnectToWiMaxManager() { LOG(INFO) << "Connected to WiMaxManager."; Error error; OnDevicesChanged(wimax_manager_proxy_->Devices(&error)); } void WiMaxProvider::DisconnectFromWiMaxManager() { SLOG(this, 2) << __func__; LOG(INFO) << "Disconnected from WiMaxManager."; OnDevicesChanged(RpcIdentifiers()); } void WiMaxProvider::OnWiMaxManagerAppeared() { SLOG(this, 2) << __func__; DisconnectFromWiMaxManager(); ConnectToWiMaxManager(); } void WiMaxProvider::OnWiMaxManagerVanished() { SLOG(this, 2) << __func__; DisconnectFromWiMaxManager(); } void WiMaxProvider::OnDeviceInfoAvailable(const string& link_name) { SLOG(this, 2) << __func__ << "(" << link_name << ")"; map<string, RpcIdentifier>::const_iterator find_it = pending_devices_.find(link_name); if (find_it != pending_devices_.end()) { RpcIdentifier path = find_it->second; CreateDevice(link_name, path); } } void WiMaxProvider::OnNetworksChanged() { SLOG(this, 2) << __func__; // Collects a set of live networks from all devices. set<RpcIdentifier> live_networks; for (const auto& device : devices_) { const set<RpcIdentifier>& networks = device.second->networks(); live_networks.insert(networks.begin(), networks.end()); } // Removes dead networks from |networks_|. for (auto it = networks_.begin(); it != networks_.end(); ) { const RpcIdentifier& path = it->first; if (ContainsKey(live_networks, path)) { ++it; } else { LOG(INFO) << "WiMAX network disappeared: " << path; it = networks_.erase(it); } } // Retrieves network info into |networks_| for the live networks. for (const auto& network : live_networks) { RetrieveNetworkInfo(network); } // Stops dead and starts live services based on the current |networks_|. StopDeadServices(); StartLiveServices(); } bool WiMaxProvider::OnServiceUnloaded(const WiMaxServiceRefPtr& service) { SLOG(this, 2) << __func__ << "(" << service->GetStorageIdentifier() << ")"; if (service->is_default()) { return false; } // Removes the service from the managed service set. The service will be // deregistered from Manager when we release ownership by returning true. services_.erase(service->GetStorageIdentifier()); return true; } // static bool WiMaxProvider::GetServiceParametersFromArgs(const KeyValueStore& args, WiMaxNetworkId* id_ptr, string* name_ptr, Error* error) { WiMaxNetworkId id = args.LookupString(WiMaxService::kNetworkIdProperty, ""); if (id.empty()) { Error::PopulateAndLog( FROM_HERE, error, Error::kInvalidArguments, "Missing WiMAX network id."); return false; } string name = args.LookupString(kNameProperty, ""); if (name.empty()) { Error::PopulateAndLog( FROM_HERE, error, Error::kInvalidArguments, "Missing WiMAX service name."); return false; } *id_ptr = id; *name_ptr = name; return true; } // static bool WiMaxProvider::GetServiceParametersFromStorage( const StoreInterface* storage, const std::string& entry_name, WiMaxNetworkId* id_ptr, std::string* name_ptr, Error* error) { string type; if (!storage->GetString(entry_name, Service::kStorageType, &type) || type != kTypeWimax) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Unspecified or invalid network type"); return false; } if (!storage->GetString(entry_name, WiMaxService::kStorageNetworkId, id_ptr) || id_ptr->empty()) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Network ID not specified"); return false; } if (!storage->GetString(entry_name, Service::kStorageName, name_ptr) || name_ptr->empty()) { Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments, "Network name not specified"); return false; } return true; } ServiceRefPtr WiMaxProvider::GetService(const KeyValueStore& args, Error* error) { SLOG(this, 2) << __func__; CHECK_EQ(kTypeWimax, args.GetString(kTypeProperty)); WiMaxNetworkId id; string name; if (!GetServiceParametersFromArgs(args, &id, &name, error)) { return nullptr; } WiMaxServiceRefPtr service = GetUniqueService(id, name); CHECK(service); // Starts the service if there's a matching live network. StartLiveServices(); return service; } ServiceRefPtr WiMaxProvider::FindSimilarService(const KeyValueStore& args, Error* error) const { SLOG(this, 2) << __func__; CHECK_EQ(kTypeWimax, args.GetString(kTypeProperty)); WiMaxNetworkId id; string name; if (!GetServiceParametersFromArgs(args, &id, &name, error)) { return nullptr; } string storage_id = WiMaxService::CreateStorageIdentifier(id, name); WiMaxServiceRefPtr service = FindService(storage_id); if (!service) { error->Populate(Error::kNotFound, "Matching service was not found"); } return service; } ServiceRefPtr WiMaxProvider::CreateTemporaryService(const KeyValueStore& args, Error* error) { SLOG(this, 2) << __func__; CHECK_EQ(kTypeWimax, args.GetString(kTypeProperty)); WiMaxNetworkId id; string name; if (!GetServiceParametersFromArgs(args, &id, &name, error)) { return nullptr; } return CreateService(id, name); } ServiceRefPtr WiMaxProvider::CreateTemporaryServiceFromProfile( const ProfileRefPtr& profile, const std::string& entry_name, Error* error) { WiMaxNetworkId id; string name; if (!GetServiceParametersFromStorage(profile->GetConstStorage(), entry_name, &id, &name, error)) { return nullptr; } return CreateService(id, name); } void WiMaxProvider::CreateServicesFromProfile(const ProfileRefPtr& profile) { SLOG(this, 2) << __func__; bool created = false; const StoreInterface* storage = profile->GetConstStorage(); KeyValueStore args; args.SetString(kTypeProperty, kTypeWimax); for (const auto& storage_id : storage->GetGroupsWithProperties(args)) { WiMaxNetworkId id; string name; if (!GetServiceParametersFromStorage(storage, storage_id, &id, &name, nullptr)) { continue; } if (FindService(storage_id)) { continue; } WiMaxServiceRefPtr service = GetUniqueService(id, name); CHECK(service); if (!profile->ConfigureService(service)) { LOG(ERROR) << "Could not configure service: " << storage_id; } created = true; } if (created) { StartLiveServices(); } } WiMaxRefPtr WiMaxProvider::SelectCarrier( const WiMaxServiceConstRefPtr& service) { SLOG(this, 2) << __func__ << "(" << service->GetStorageIdentifier() << ")"; if (devices_.empty()) { LOG(ERROR) << "No WiMAX devices available."; return nullptr; } // TODO(petkov): For now, just return the first available device. We need to // be smarter here and select a device that sees |service|'s network. return devices_.begin()->second; } void WiMaxProvider::OnDevicesChanged(const RpcIdentifiers& devices) { SLOG(this, 2) << __func__; DestroyDeadDevices(devices); for (const auto& path : devices) { string link_name = GetLinkName(path); if (!link_name.empty()) { CreateDevice(link_name, path); } } } void WiMaxProvider::CreateDevice(const string& link_name, const RpcIdentifier& path) { SLOG(this, 2) << __func__ << "(" << link_name << ", " << path << ")"; if (ContainsKey(devices_, link_name)) { SLOG(this, 2) << "Device already exists."; CHECK_EQ(path, devices_[link_name]->path()); return; } pending_devices_.erase(link_name); DeviceInfo* device_info = manager_->device_info(); if (device_info->IsDeviceBlackListed(link_name)) { LOG(INFO) << "WiMAX device not created, interface blacklisted: " << link_name; return; } int index = device_info->GetIndex(link_name); if (index < 0) { SLOG(this, 2) << link_name << " pending device info."; // Adds the link to the pending device map, waiting for a notification from // DeviceInfo that it's received information about the device from RTNL. pending_devices_[link_name] = path; return; } ByteString address_bytes; if (!device_info->GetMACAddress(index, &address_bytes)) { LOG(ERROR) << "Unable to create a WiMAX device with no MAC address: " << link_name; return; } string address = address_bytes.HexEncode(); WiMaxRefPtr device(new WiMax(control_, dispatcher_, metrics_, manager_, link_name, address, index, path)); devices_[link_name] = device; device_info->RegisterDevice(device); LOG(INFO) << "Created WiMAX device: " << link_name << " @ " << path; } void WiMaxProvider::DestroyDeadDevices(const RpcIdentifiers& live_devices) { SLOG(this, 2) << __func__ << "(" << live_devices.size() << ")"; for (auto it = pending_devices_.begin(); it != pending_devices_.end(); ) { if (find(live_devices.begin(), live_devices.end(), it->second) == live_devices.end()) { LOG(INFO) << "Forgetting pending device: " << it->second; it = pending_devices_.erase(it); } else { ++it; } } for (auto it = devices_.begin(); it != devices_.end(); ) { if (find(live_devices.begin(), live_devices.end(), it->second->path()) == live_devices.end()) { LOG(INFO) << "Destroying device: " << it->first; const WiMaxRefPtr& device = it->second; device->OnDeviceVanished(); manager_->device_info()->DeregisterDevice(device); it = devices_.erase(it); } else { ++it; } } } string WiMaxProvider::GetLinkName(const RpcIdentifier& path) { if (base::StartsWith(path, wimax_manager::kDeviceObjectPathPrefix, base::CompareCase::SENSITIVE)) { return path.substr(strlen(wimax_manager::kDeviceObjectPathPrefix)); } LOG(ERROR) << "Unable to determine link name from RPC path: " << path; return string(); } void WiMaxProvider::RetrieveNetworkInfo(const RpcIdentifier& path) { if (ContainsKey(networks_, path)) { // Nothing to do, the network info is already available. return; } LOG(INFO) << "WiMAX network appeared: " << path; std::unique_ptr<WiMaxNetworkProxyInterface> proxy( control_->CreateWiMaxNetworkProxy(path)); Error error; NetworkInfo info; info.name = proxy->Name(&error); if (error.IsFailure()) { return; } uint32_t identifier = proxy->Identifier(&error); if (error.IsFailure()) { return; } info.id = WiMaxService::ConvertIdentifierToNetworkId(identifier); networks_[path] = info; } WiMaxServiceRefPtr WiMaxProvider::FindService(const string& storage_id) const { SLOG(this, 2) << __func__ << "(" << storage_id << ")"; map<string, WiMaxServiceRefPtr>::const_iterator find_it = services_.find(storage_id); if (find_it == services_.end()) { return nullptr; } const WiMaxServiceRefPtr& service = find_it->second; LOG_IF(ERROR, storage_id != service->GetStorageIdentifier()); return service; } WiMaxServiceRefPtr WiMaxProvider::GetUniqueService(const WiMaxNetworkId& id, const string& name) { SLOG(this, 2) << __func__ << "(" << id << ", " << name << ")"; string storage_id = WiMaxService::CreateStorageIdentifier(id, name); WiMaxServiceRefPtr service = FindService(storage_id); if (service) { SLOG(this, 2) << "Service already exists."; return service; } service = CreateService(id, name); services_[service->GetStorageIdentifier()] = service; manager_->RegisterService(service); LOG(INFO) << "Registered WiMAX service: " << service->GetStorageIdentifier(); return service; } WiMaxServiceRefPtr WiMaxProvider::CreateService(const WiMaxNetworkId& id, const string& name) { WiMaxServiceRefPtr service = new WiMaxService(control_, dispatcher_, metrics_, manager_); service->set_network_id(id); service->set_friendly_name(name); service->InitStorageIdentifier(); return service; } void WiMaxProvider::StartLiveServices() { SLOG(this, 2) << __func__ << "(" << networks_.size() << ")"; for (const auto& nit : networks_) { const RpcIdentifier& path = nit.first; const NetworkInfo& info = nit.second; // Creates the default service for the network, if not already created. GetUniqueService(info.id, info.name)->set_is_default(true); // Starts services for this live network. for (const auto& entry : services_) { const WiMaxServiceRefPtr& service = entry.second; if (service->network_id() != info.id || service->IsStarted()) { continue; } if (!service->Start(control_->CreateWiMaxNetworkProxy(path))) { LOG(ERROR) << "Unable to start service: " << service->GetStorageIdentifier(); } } } } void WiMaxProvider::StopDeadServices() { SLOG(this, 2) << __func__ << "(" << networks_.size() << ")"; for (map<string, WiMaxServiceRefPtr>::iterator it = services_.begin(); it != services_.end(); ) { // Keeps a local reference until we're done with this service. WiMaxServiceRefPtr service = it->second; if (service->IsStarted() && !ContainsKey(networks_, service->GetNetworkObjectPath())) { service->Stop(); // Default services are created and registered when a network becomes // live. They need to be deregistered and destroyed when the network // disappears. if (service->is_default()) { // Removes |service| from the managed service set before deregistering // it from Manager to ensure correct iterator increment (consider // Manager::DeregisterService -> WiMaxService::Unload -> // WiMaxProvider::OnServiceUnloaded -> services_.erase). services_.erase(it++); manager_->DeregisterService(service); continue; } } ++it; } } void WiMaxProvider::DestroyAllServices() { SLOG(this, 2) << __func__; while (!services_.empty()) { // Keeps a local reference until we're done with this service. WiMaxServiceRefPtr service = services_.begin()->second; services_.erase(services_.begin()); // Stops the service so that it can notify its carrier device, if any. service->Stop(); manager_->DeregisterService(service); LOG(INFO) << "Deregistered WiMAX service: " << service->GetStorageIdentifier(); } } } // namespace shill