geolocation_handler.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/network/geolocation_handler.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/bind.h"
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/string_number_conversions.h"
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/values.h"
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/dbus/dbus_thread_manager.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/dbus/shill_manager_client.h"
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "third_party/cros_system_api/dbus/service_constants.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace chromeos {
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)static GeolocationHandler* g_geolocation_handler = NULL;
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)GeolocationHandler::GeolocationHandler()
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : wifi_enabled_(false),
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)GeolocationHandler::~GeolocationHandler() {
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ShillManagerClient* manager_client =
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DBusThreadManager::Get()->GetShillManagerClient();
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (manager_client)
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    manager_client->RemovePropertyChangedObserver(this);
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void GeolocationHandler::Init() {
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ShillManagerClient* manager_client =
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DBusThreadManager::Get()->GetShillManagerClient();
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  manager_client->GetProperties(
3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      base::Bind(&GeolocationHandler::ManagerPropertiesCallback,
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
36868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  manager_client->AddPropertyChangedObserver(this);
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void GeolocationHandler::Initialize() {
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CHECK(!g_geolocation_handler);
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  g_geolocation_handler = new GeolocationHandler();
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  g_geolocation_handler->Init();
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void GeolocationHandler::Shutdown() {
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CHECK(g_geolocation_handler);
49868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  delete g_geolocation_handler;
50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  g_geolocation_handler = NULL;
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)GeolocationHandler* GeolocationHandler::Get() {
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CHECK(g_geolocation_handler)
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      << "GeolocationHandler::Get() called before Initialize()";
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return g_geolocation_handler;
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool GeolocationHandler::GetWifiAccessPoints(
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    WifiAccessPointVector* access_points, int64* age_ms) {
6290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (!wifi_enabled_)
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Always request updated access points.
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  RequestWifiAccessPoints();
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If no data has been received, return false.
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (geolocation_received_time_.is_null())
6890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return false;
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (access_points)
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    *access_points = wifi_access_points_;
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (age_ms) {
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::TimeDelta dtime = base::Time::Now() - geolocation_received_time_;
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    *age_ms = dtime.InMilliseconds();
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return true;
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void GeolocationHandler::OnPropertyChanged(const std::string& key,
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                           const base::Value& value) {
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  HandlePropertyChanged(key, value);
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//------------------------------------------------------------------------------
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Private methods
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void GeolocationHandler::ManagerPropertiesCallback(
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DBusMethodCallStatus call_status,
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const base::DictionaryValue& properties) {
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const base::Value* value = NULL;
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (properties.Get(flimflam::kEnabledTechnologiesProperty, &value) && value)
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    HandlePropertyChanged(flimflam::kEnabledTechnologiesProperty, *value);
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void GeolocationHandler::HandlePropertyChanged(const std::string& key,
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                               const base::Value& value) {
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (key != flimflam::kEnabledTechnologiesProperty)
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const base::ListValue* technologies = NULL;
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!value.GetAsList(&technologies) || !technologies)
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool wifi_was_enabled = wifi_enabled_;
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  wifi_enabled_ = false;
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (base::ListValue::const_iterator iter = technologies->begin();
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       iter != technologies->end(); ++iter) {
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    std::string technology;
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    (*iter)->GetAsString(&technology);
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (technology == flimflam::kTypeWifi) {
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      wifi_enabled_ = true;
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!wifi_was_enabled && wifi_enabled_)
113868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    RequestWifiAccessPoints();  // Request initial location data.
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void GeolocationHandler::RequestWifiAccessPoints() {
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DBusThreadManager::Get()->GetShillManagerClient()->GetNetworksForGeolocation(
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&GeolocationHandler::GeolocationCallback,
119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void GeolocationHandler::GeolocationCallback(
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DBusMethodCallStatus call_status,
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const base::DictionaryValue& properties) {
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    LOG(ERROR) << "Failed to get Geolocation data: " << call_status;
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  wifi_access_points_.clear();
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (properties.empty())
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;  // No enabled devices, don't update received time.
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Dictionary<device_type, entry_list>
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (base::DictionaryValue::Iterator iter(properties);
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       iter.HasNext(); iter.Advance()) {
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const base::ListValue* entry_list = NULL;
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (!iter.value().GetAsList(&entry_list)) {
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      LOG(WARNING) << "Geolocation dictionary value not a List: " << iter.key();
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      continue;
1407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    }
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // List[Dictionary<key, value_str>]
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (size_t i = 0; i < entry_list->GetSize(); ++i) {
1437d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      const base::DictionaryValue* entry = NULL;
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (!entry_list->GetDictionary(i, &entry) || !entry) {
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        LOG(WARNING) << "Geolocation list value not a Dictionary: " << i;
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        continue;
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // Docs: developers.google.com/maps/documentation/business/geolocation
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      WifiAccessPoint wap;
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      entry->GetString(shill::kGeoMacAddressProperty, &wap.mac_address);
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::string age_str;
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (entry->GetString(shill::kGeoAgeProperty, &age_str)) {
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        int64 age_ms;
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (base::StringToInt64(age_str, &age_ms)) {
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          wap.timestamp =
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              base::Time::Now() - base::TimeDelta::FromMilliseconds(age_ms);
1577d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        }
1587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      }
1597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      std::string strength_str;
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (entry->GetString(shill::kGeoSignalStrengthProperty, &strength_str))
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::StringToInt(strength_str, &wap.signal_strength);
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::string signal_str;
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (entry->GetString(shill::kGeoSignalToNoiseRatioProperty, &signal_str))
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::StringToInt(signal_str, &wap.signal_to_noise);
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::string channel_str;
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (entry->GetString(shill::kGeoChannelProperty, &channel_str))
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        base::StringToInt(channel_str, &wap.channel);
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      wifi_access_points_.push_back(wap);
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
1707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  geolocation_received_time_ = base::Time::Now();
172c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
1737d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace chromeos
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)