// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Provides wifi scan API binding for chromeos, using proprietary APIs. #include "chrome/browser/geolocation/wifi_data_provider_chromeos.h" #include "base/bind.h" #include "base/utf_string_conversions.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/network_library.h" #include "content/public/browser/browser_thread.h" using content::BrowserThread; namespace { // The time periods between successive polls of the wifi data. const int kDefaultPollingIntervalMilliseconds = 10 * 1000; // 10s const int kNoChangePollingIntervalMilliseconds = 2 * 60 * 1000; // 2 mins const int kTwoNoChangePollingIntervalMilliseconds = 10 * 60 * 1000; // 10 mins const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s WifiDataProviderImplBase* ChromeOSFactoryFunction() { return new WifiDataProviderChromeOs(); } // This global class forces code that links in this file to use that as a data // provider instead of the default Linux provider. class RegisterChromeOSWifiDataProvider { public: RegisterChromeOSWifiDataProvider() { WifiDataProvider::SetFactory(ChromeOSFactoryFunction); } }; RegisterChromeOSWifiDataProvider g_force_chrome_os_provider; } // namespace namespace chromeos { namespace { // Wifi API binding to network_library.h, to allow reuse of the polling behavior // defined in WifiDataProviderCommon. class NetworkLibraryWlanApi : public WifiDataProviderCommon::WlanApiInterface { public: // Does not transfer ownership, |lib| must remain valid for lifetime of // this object. explicit NetworkLibraryWlanApi(NetworkLibrary* lib); ~NetworkLibraryWlanApi(); // WifiDataProviderCommon::WlanApiInterface bool GetAccessPointData(WifiData::AccessPointDataSet* data); private: NetworkLibrary* network_library_; DISALLOW_COPY_AND_ASSIGN(NetworkLibraryWlanApi); }; NetworkLibraryWlanApi::NetworkLibraryWlanApi(NetworkLibrary* lib) : network_library_(lib) { DCHECK(network_library_ != NULL); } NetworkLibraryWlanApi::~NetworkLibraryWlanApi() { } bool NetworkLibraryWlanApi::GetAccessPointData( WifiData::AccessPointDataSet* result) { WifiAccessPointVector access_points; if (!network_library_->GetWifiAccessPoints(&access_points)) return false; for (WifiAccessPointVector::const_iterator i = access_points.begin(); i != access_points.end(); ++i) { AccessPointData ap_data; ap_data.mac_address = ASCIIToUTF16(i->mac_address); ap_data.radio_signal_strength = i->signal_strength; ap_data.channel = i->channel; ap_data.signal_to_noise = i->signal_to_noise; ap_data.ssid = UTF8ToUTF16(i->name); result->insert(ap_data); } return !result->empty() || network_library_->wifi_enabled(); } } // namespace } // namespace chromeos WifiDataProviderChromeOs::WifiDataProviderChromeOs() : started_(false) { } WifiDataProviderChromeOs::~WifiDataProviderChromeOs() { } bool WifiDataProviderChromeOs::StartDataProvider() { DCHECK(CalledOnClientThread()); DCHECK(polling_policy_ == NULL); polling_policy_.reset(NewPollingPolicy()); DCHECK(polling_policy_ != NULL); ScheduleStart(); return true; } void WifiDataProviderChromeOs::StopDataProvider() { DCHECK(CalledOnClientThread()); polling_policy_.reset(); ScheduleStop(); } bool WifiDataProviderChromeOs::GetData(WifiData* data) { DCHECK(CalledOnClientThread()); DCHECK(data); *data = wifi_data_; return is_first_scan_complete_; } WifiDataProviderCommon::WlanApiInterface* WifiDataProviderChromeOs::NewWlanApi(chromeos::NetworkLibrary* lib) { return new chromeos::NetworkLibraryWlanApi(lib); } WifiDataProviderCommon::WlanApiInterface* WifiDataProviderChromeOs::NewWlanApi() { chromeos::CrosLibrary* cros_lib = chromeos::CrosLibrary::Get(); DCHECK(cros_lib); return NewWlanApi(cros_lib->GetNetworkLibrary()); } PollingPolicyInterface* WifiDataProviderChromeOs::NewPollingPolicy() { return new GenericPollingPolicy; } void WifiDataProviderChromeOs::DoStartTaskOnUIThread() { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); wlan_api_.reset(NewWlanApi()); if (wlan_api_ == NULL) { client_loop()->PostTask( FROM_HERE, base::Bind(&WifiDataProviderChromeOs::DidStartFailed, this)); return; } DoWifiScanTaskOnUIThread(); } void WifiDataProviderChromeOs::DoStopTaskOnUIThread() { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); wlan_api_.reset(); } void WifiDataProviderChromeOs::DidStartFailed() { CHECK(CalledOnClientThread()); // Error! Can't do scans, so don't try and schedule one. is_first_scan_complete_ = true; } void WifiDataProviderChromeOs::DoWifiScanTaskOnUIThread() { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // This method could be scheduled after a DoStopTaskOnUIThread. if (!wlan_api_.get()) return; WifiData new_data; if (!wlan_api_->GetAccessPointData(&new_data.access_point_data)) { client_loop()->PostTask( FROM_HERE, base::Bind(&WifiDataProviderChromeOs::DidWifiScanTaskNoResults, this)); } else { client_loop()->PostTask( FROM_HERE, base::Bind(&WifiDataProviderChromeOs::DidWifiScanTask, this, new_data)); } } void WifiDataProviderChromeOs::DidWifiScanTaskNoResults() { DCHECK(CalledOnClientThread()); // Schedule next scan if started (StopDataProvider could have been called // in between DoWifiScanTaskOnUIThread and this method). if (started_) ScheduleNextScan(polling_policy_->NoWifiInterval()); MaybeNotifyListeners(false); } void WifiDataProviderChromeOs::DidWifiScanTask(const WifiData& new_data) { DCHECK(CalledOnClientThread()); bool update_available = wifi_data_.DiffersSignificantly(new_data); wifi_data_ = new_data; // Schedule next scan if started (StopDataProvider could have been called // in between DoWifiScanTaskOnUIThread and this method). if (started_) { polling_policy_->UpdatePollingInterval(update_available); ScheduleNextScan(polling_policy_->PollingInterval()); } MaybeNotifyListeners(update_available); } void WifiDataProviderChromeOs::MaybeNotifyListeners(bool update_available) { if (update_available || !is_first_scan_complete_) { is_first_scan_complete_ = true; NotifyListeners(); } } void WifiDataProviderChromeOs::ScheduleNextScan(int interval) { DCHECK(CalledOnClientThread()); DCHECK(started_); BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::Bind(&WifiDataProviderChromeOs::DoWifiScanTaskOnUIThread, this), interval); } void WifiDataProviderChromeOs::ScheduleStop() { DCHECK(CalledOnClientThread()); DCHECK(started_); started_ = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&WifiDataProviderChromeOs::DoStopTaskOnUIThread, this)); } void WifiDataProviderChromeOs::ScheduleStart() { DCHECK(CalledOnClientThread()); DCHECK(!started_); started_ = true; // Perform first scan ASAP regardless of the polling policy. If this scan // fails we'll retry at a rate in line with the polling policy. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&WifiDataProviderChromeOs::DoStartTaskOnUIThread, this)); }