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