[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 1 | // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "chromeos/network/geolocation_handler.h" |
| 6 | |
avi | 6e1a22d | 2015-12-21 03:43:20 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | #include <stdint.h> |
| 9 | |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 10 | #include "base/bind.h" |
[email protected] | afa339d7 | 2013-06-11 06:32:51 | [diff] [blame] | 11 | #include "base/strings/string_number_conversions.h" |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 12 | #include "base/values.h" |
| 13 | #include "chromeos/dbus/dbus_thread_manager.h" |
| 14 | #include "chromeos/dbus/shill_manager_client.h" |
| 15 | #include "third_party/cros_system_api/dbus/service_constants.h" |
| 16 | |
| 17 | namespace chromeos { |
| 18 | |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 19 | namespace { |
| 20 | |
| 21 | constexpr const char* kDevicePropertyNames[] = { |
| 22 | shill::kGeoWifiAccessPointsProperty, shill::kGeoCellTowersProperty}; |
| 23 | |
Niranjan Kumar | 4c2ce9f | 2017-10-03 22:28:51 | [diff] [blame] | 24 | std::string HexToDecimal(std::string hex_str) { |
| 25 | return std::to_string(std::stoi(hex_str, nullptr, 16)); |
| 26 | } |
| 27 | |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 28 | } // namespace |
| 29 | |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 30 | GeolocationHandler::GeolocationHandler() |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 31 | : cellular_enabled_(false), wifi_enabled_(false), weak_ptr_factory_(this) {} |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 32 | |
| 33 | GeolocationHandler::~GeolocationHandler() { |
| 34 | ShillManagerClient* manager_client = |
| 35 | DBusThreadManager::Get()->GetShillManagerClient(); |
| 36 | if (manager_client) |
| 37 | manager_client->RemovePropertyChangedObserver(this); |
| 38 | } |
| 39 | |
| 40 | void GeolocationHandler::Init() { |
| 41 | ShillManagerClient* manager_client = |
| 42 | DBusThreadManager::Get()->GetShillManagerClient(); |
| 43 | manager_client->GetProperties( |
| 44 | base::Bind(&GeolocationHandler::ManagerPropertiesCallback, |
| 45 | weak_ptr_factory_.GetWeakPtr())); |
| 46 | manager_client->AddPropertyChangedObserver(this); |
| 47 | } |
| 48 | |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 49 | bool GeolocationHandler::GetWifiAccessPoints( |
avi | 6e1a22d | 2015-12-21 03:43:20 | [diff] [blame] | 50 | WifiAccessPointVector* access_points, |
| 51 | int64_t* age_ms) { |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 52 | if (!wifi_enabled_) |
| 53 | return false; |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 54 | // Always request updated info. |
| 55 | RequestGeolocationObjects(); |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 56 | // If no data has been received, return false. |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 57 | if (geolocation_received_time_.is_null() || wifi_access_points_.size() == 0) |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 58 | return false; |
| 59 | if (access_points) |
| 60 | *access_points = wifi_access_points_; |
| 61 | if (age_ms) { |
| 62 | base::TimeDelta dtime = base::Time::Now() - geolocation_received_time_; |
| 63 | *age_ms = dtime.InMilliseconds(); |
| 64 | } |
| 65 | return true; |
| 66 | } |
| 67 | |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 68 | bool GeolocationHandler::GetNetworkInformation( |
| 69 | WifiAccessPointVector* access_points, |
| 70 | CellTowerVector* cell_towers) { |
| 71 | if (!cellular_enabled_ && !wifi_enabled_) |
| 72 | return false; |
| 73 | |
| 74 | // Always request updated info. |
| 75 | RequestGeolocationObjects(); |
| 76 | |
| 77 | // If no data has been received, return false. |
| 78 | if (geolocation_received_time_.is_null()) |
| 79 | return false; |
| 80 | |
| 81 | if (cell_towers) |
| 82 | *cell_towers = cell_towers_; |
| 83 | if (access_points) |
| 84 | *access_points = wifi_access_points_; |
| 85 | |
| 86 | return true; |
| 87 | } |
| 88 | |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 89 | void GeolocationHandler::OnPropertyChanged(const std::string& key, |
| 90 | const base::Value& value) { |
| 91 | HandlePropertyChanged(key, value); |
| 92 | } |
| 93 | |
| 94 | //------------------------------------------------------------------------------ |
| 95 | // Private methods |
| 96 | |
| 97 | void GeolocationHandler::ManagerPropertiesCallback( |
| 98 | DBusMethodCallStatus call_status, |
| 99 | const base::DictionaryValue& properties) { |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 100 | const base::Value* value = nullptr; |
[email protected] | 547396c5 | 2013-09-24 07:24:56 | [diff] [blame] | 101 | if (properties.Get(shill::kEnabledTechnologiesProperty, &value) && value) |
| 102 | HandlePropertyChanged(shill::kEnabledTechnologiesProperty, *value); |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | void GeolocationHandler::HandlePropertyChanged(const std::string& key, |
| 106 | const base::Value& value) { |
[email protected] | 547396c5 | 2013-09-24 07:24:56 | [diff] [blame] | 107 | if (key != shill::kEnabledTechnologiesProperty) |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 108 | return; |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 109 | const base::ListValue* technologies = nullptr; |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 110 | if (!value.GetAsList(&technologies) || !technologies) |
| 111 | return; |
| 112 | bool wifi_was_enabled = wifi_enabled_; |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 113 | bool cellular_was_enabled = cellular_enabled_; |
| 114 | cellular_enabled_ = false; |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 115 | wifi_enabled_ = false; |
| 116 | for (base::ListValue::const_iterator iter = technologies->begin(); |
| 117 | iter != technologies->end(); ++iter) { |
| 118 | std::string technology; |
jdoerrie | a5676c6 | 2017-04-11 18:09:14 | [diff] [blame] | 119 | iter->GetAsString(&technology); |
[email protected] | 547396c5 | 2013-09-24 07:24:56 | [diff] [blame] | 120 | if (technology == shill::kTypeWifi) { |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 121 | wifi_enabled_ = true; |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 122 | } else if (technology == shill::kTypeCellular) { |
| 123 | cellular_enabled_ = true; |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 124 | } |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 125 | if (wifi_enabled_ && cellular_enabled_) |
| 126 | break; |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 127 | } |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 128 | |
| 129 | // Request initial location data. |
| 130 | if ((!wifi_was_enabled && wifi_enabled_) || |
| 131 | (!cellular_was_enabled && cellular_enabled_)) { |
| 132 | RequestGeolocationObjects(); |
| 133 | } |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 134 | } |
| 135 | |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 136 | void GeolocationHandler::RequestGeolocationObjects() { |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 137 | DBusThreadManager::Get()->GetShillManagerClient()->GetNetworksForGeolocation( |
| 138 | base::Bind(&GeolocationHandler::GeolocationCallback, |
| 139 | weak_ptr_factory_.GetWeakPtr())); |
| 140 | } |
| 141 | |
| 142 | void GeolocationHandler::GeolocationCallback( |
| 143 | DBusMethodCallStatus call_status, |
| 144 | const base::DictionaryValue& properties) { |
| 145 | if (call_status != DBUS_METHOD_CALL_SUCCESS) { |
| 146 | LOG(ERROR) << "Failed to get Geolocation data: " << call_status; |
| 147 | return; |
| 148 | } |
| 149 | wifi_access_points_.clear(); |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 150 | cell_towers_.clear(); |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 151 | if (properties.empty()) |
| 152 | return; // No enabled devices, don't update received time. |
| 153 | |
| 154 | // Dictionary<device_type, entry_list> |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 155 | // Example dict returned from shill: |
| 156 | // { |
| 157 | // kGeoWifiAccessPointsProperty: [ {kGeoMacAddressProperty: mac_value, ...}, |
| 158 | // ... |
| 159 | // ], |
| 160 | // kGeoCellTowersProperty: [ {kGeoCellIdProperty: cell_id_value, ...}, ... ] |
| 161 | // } |
vmpstr | 843b41a | 2017-03-01 21:15:03 | [diff] [blame] | 162 | for (auto* device_type : kDevicePropertyNames) { |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 163 | if (!properties.HasKey(device_type)) { |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 164 | continue; |
| 165 | } |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 166 | |
| 167 | const base::ListValue* entry_list = nullptr; |
| 168 | if (!properties.GetList(device_type, &entry_list)) { |
| 169 | LOG(WARNING) << "Geolocation dictionary value not a List: " |
| 170 | << device_type; |
| 171 | continue; |
| 172 | } |
| 173 | |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 174 | // List[Dictionary<key, value_str>] |
| 175 | for (size_t i = 0; i < entry_list->GetSize(); ++i) { |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 176 | const base::DictionaryValue* entry = nullptr; |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 177 | if (!entry_list->GetDictionary(i, &entry) || !entry) { |
| 178 | LOG(WARNING) << "Geolocation list value not a Dictionary: " << i; |
| 179 | continue; |
| 180 | } |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 181 | if (device_type == shill::kGeoWifiAccessPointsProperty) { |
| 182 | AddAccessPointFromDict(entry); |
| 183 | } else if (device_type == shill::kGeoCellTowersProperty) { |
| 184 | AddCellTowerFromDict(entry); |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 185 | } |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 186 | } |
| 187 | } |
| 188 | geolocation_received_time_ = base::Time::Now(); |
| 189 | } |
| 190 | |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 191 | void GeolocationHandler::AddAccessPointFromDict( |
| 192 | const base::DictionaryValue* entry) { |
| 193 | // Docs: developers.google.com/maps/documentation/business/geolocation |
| 194 | WifiAccessPoint wap; |
| 195 | |
| 196 | std::string age_str; |
| 197 | if (entry->GetString(shill::kGeoAgeProperty, &age_str)) { |
| 198 | int64_t age_ms; |
| 199 | if (base::StringToInt64(age_str, &age_ms)) { |
| 200 | wap.timestamp = |
| 201 | base::Time::Now() - base::TimeDelta::FromMilliseconds(age_ms); |
| 202 | } |
| 203 | } |
| 204 | entry->GetString(shill::kGeoMacAddressProperty, &wap.mac_address); |
| 205 | |
| 206 | std::string strength_str; |
| 207 | if (entry->GetString(shill::kGeoSignalStrengthProperty, &strength_str)) |
| 208 | base::StringToInt(strength_str, &wap.signal_strength); |
| 209 | |
| 210 | std::string signal_str; |
| 211 | if (entry->GetString(shill::kGeoSignalToNoiseRatioProperty, &signal_str)) { |
| 212 | base::StringToInt(signal_str, &wap.signal_to_noise); |
| 213 | } |
| 214 | |
| 215 | std::string channel_str; |
| 216 | if (entry->GetString(shill::kGeoChannelProperty, &channel_str)) |
| 217 | base::StringToInt(channel_str, &wap.channel); |
| 218 | |
| 219 | wifi_access_points_.push_back(wap); |
| 220 | } |
| 221 | |
| 222 | void GeolocationHandler::AddCellTowerFromDict( |
| 223 | const base::DictionaryValue* entry) { |
| 224 | // Docs: developers.google.com/maps/documentation/business/geolocation |
Niranjan Kumar | 4c2ce9f | 2017-10-03 22:28:51 | [diff] [blame] | 225 | |
| 226 | // Create object. |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 227 | CellTower ct; |
| 228 | |
Niranjan Kumar | 4c2ce9f | 2017-10-03 22:28:51 | [diff] [blame] | 229 | // Read time fields into object. |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 230 | std::string age_str; |
| 231 | if (entry->GetString(shill::kGeoAgeProperty, &age_str)) { |
| 232 | int64_t age_ms; |
| 233 | if (base::StringToInt64(age_str, &age_ms)) { |
| 234 | ct.timestamp = |
| 235 | base::Time::Now() - base::TimeDelta::FromMilliseconds(age_ms); |
| 236 | } |
| 237 | } |
Niranjan Kumar | 4c2ce9f | 2017-10-03 22:28:51 | [diff] [blame] | 238 | |
| 239 | // Read hex fields into object. |
| 240 | std::string hex_cell_id; |
| 241 | if (entry->GetString(shill::kGeoCellIdProperty, &hex_cell_id)) { |
| 242 | ct.ci = HexToDecimal(hex_cell_id); |
| 243 | } |
| 244 | |
| 245 | std::string hex_lac; |
| 246 | if (entry->GetString(shill::kGeoLocationAreaCodeProperty, &hex_lac)) { |
| 247 | ct.lac = HexToDecimal(hex_lac); |
| 248 | } |
| 249 | |
| 250 | // Read decimal fields into object. |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 251 | entry->GetString(shill::kGeoMobileCountryCodeProperty, &ct.mcc); |
| 252 | entry->GetString(shill::kGeoMobileNetworkCodeProperty, &ct.mnc); |
| 253 | |
Niranjan Kumar | 4c2ce9f | 2017-10-03 22:28:51 | [diff] [blame] | 254 | // Add new object to vector. |
skylarc | eaf50cb | 2017-02-14 01:16:28 | [diff] [blame] | 255 | cell_towers_.push_back(ct); |
| 256 | } |
| 257 | |
[email protected] | b7c377e | 2013-01-15 10:00:39 | [diff] [blame] | 258 | } // namespace chromeos |