12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chromeos/network/geolocation_handler.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/values.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chromeos/dbus/dbus_thread_manager.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chromeos/dbus/shill_manager_client.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "third_party/cros_system_api/dbus/service_constants.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace chromeos {
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GeolocationHandler::GeolocationHandler()
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : wifi_enabled_(false),
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GeolocationHandler::~GeolocationHandler() {
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ShillManagerClient* manager_client =
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DBusThreadManager::Get()->GetShillManagerClient();
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (manager_client)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    manager_client->RemovePropertyChangedObserver(this);
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GeolocationHandler::Init() {
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ShillManagerClient* manager_client =
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DBusThreadManager::Get()->GetShillManagerClient();
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  manager_client->GetProperties(
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&GeolocationHandler::ManagerPropertiesCallback,
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  manager_client->AddPropertyChangedObserver(this);
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool GeolocationHandler::GetWifiAccessPoints(
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    WifiAccessPointVector* access_points, int64* age_ms) {
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!wifi_enabled_)
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Always request updated access points.
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RequestWifiAccessPoints();
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If no data has been received, return false.
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (geolocation_received_time_.is_null())
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (access_points)
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *access_points = wifi_access_points_;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (age_ms) {
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::TimeDelta dtime = base::Time::Now() - geolocation_received_time_;
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *age_ms = dtime.InMilliseconds();
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GeolocationHandler::OnPropertyChanged(const std::string& key,
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                           const base::Value& value) {
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HandlePropertyChanged(key, value);
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)//------------------------------------------------------------------------------
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Private methods
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GeolocationHandler::ManagerPropertiesCallback(
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DBusMethodCallStatus call_status,
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::DictionaryValue& properties) {
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::Value* value = NULL;
6768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (properties.Get(shill::kEnabledTechnologiesProperty, &value) && value)
6868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    HandlePropertyChanged(shill::kEnabledTechnologiesProperty, *value);
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GeolocationHandler::HandlePropertyChanged(const std::string& key,
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                               const base::Value& value) {
7368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (key != shill::kEnabledTechnologiesProperty)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::ListValue* technologies = NULL;
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!value.GetAsList(&technologies) || !technologies)
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool wifi_was_enabled = wifi_enabled_;
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  wifi_enabled_ = false;
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (base::ListValue::const_iterator iter = technologies->begin();
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       iter != technologies->end(); ++iter) {
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string technology;
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    (*iter)->GetAsString(&technology);
8468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    if (technology == shill::kTypeWifi) {
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      wifi_enabled_ = true;
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!wifi_was_enabled && wifi_enabled_)
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RequestWifiAccessPoints();  // Request initial location data.
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GeolocationHandler::RequestWifiAccessPoints() {
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DBusThreadManager::Get()->GetShillManagerClient()->GetNetworksForGeolocation(
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&GeolocationHandler::GeolocationCallback,
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void GeolocationHandler::GeolocationCallback(
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DBusMethodCallStatus call_status,
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::DictionaryValue& properties) {
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (call_status != DBUS_METHOD_CALL_SUCCESS) {
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    LOG(ERROR) << "Failed to get Geolocation data: " << call_status;
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  wifi_access_points_.clear();
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (properties.empty())
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;  // No enabled devices, don't update received time.
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Dictionary<device_type, entry_list>
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (base::DictionaryValue::Iterator iter(properties);
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       !iter.IsAtEnd(); iter.Advance()) {
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::ListValue* entry_list = NULL;
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!iter.value().GetAsList(&entry_list)) {
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      LOG(WARNING) << "Geolocation dictionary value not a List: " << iter.key();
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // List[Dictionary<key, value_str>]
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (size_t i = 0; i < entry_list->GetSize(); ++i) {
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const base::DictionaryValue* entry = NULL;
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!entry_list->GetDictionary(i, &entry) || !entry) {
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        LOG(WARNING) << "Geolocation list value not a Dictionary: " << i;
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        continue;
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Docs: developers.google.com/maps/documentation/business/geolocation
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      WifiAccessPoint wap;
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      entry->GetString(shill::kGeoMacAddressProperty, &wap.mac_address);
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::string age_str;
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (entry->GetString(shill::kGeoAgeProperty, &age_str)) {
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        int64 age_ms;
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (base::StringToInt64(age_str, &age_ms)) {
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          wap.timestamp =
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              base::Time::Now() - base::TimeDelta::FromMilliseconds(age_ms);
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::string strength_str;
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (entry->GetString(shill::kGeoSignalStrengthProperty, &strength_str))
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::StringToInt(strength_str, &wap.signal_strength);
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::string signal_str;
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (entry->GetString(shill::kGeoSignalToNoiseRatioProperty, &signal_str))
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::StringToInt(signal_str, &wap.signal_to_noise);
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::string channel_str;
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (entry->GetString(shill::kGeoChannelProperty, &channel_str))
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::StringToInt(channel_str, &wap.channel);
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      wifi_access_points_.push_back(wap);
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  geolocation_received_time_ = base::Time::Now();
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace chromeos
152