1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_GEOLOCATION_GEOLOCATION_CONTROLLER_H_
#define ASH_SYSTEM_GEOLOCATION_GEOLOCATION_CONTROLLER_H_
#include <memory>
#include <string>
#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_observer.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/types/expected.h"
#include "chromeos/ash/components/geolocation/simple_geolocation_provider.h"
#include "chromeos/ash/components/settings/timezone_settings.h"
#include "chromeos/dbus/power/power_manager_client.h"
class PrefChangeRegistrar;
class PrefRegistrySimple;
class PrefService;
namespace base {
class Clock;
} // namespace base
namespace ash {
class LocalTimeConverter;
// Represents a geolocation position fix. It's "simple" because it doesn't
// expose all the parameters of the position interface as defined by the
// Geolocation API Specification:
// https://dev.w3.org/geo/api/spec-source.html#position_interface
// The GeolocationController is only interested in valid latitude and
// longitude. It also doesn't require any specific accuracy. The more accurate
// the positions, the more accurate sunset and sunrise times calculations.
// However, an IP-based geoposition is considered good enough.
struct SimpleGeoposition {
bool operator==(const SimpleGeoposition& other) const {
return latitude == other.latitude && longitude == other.longitude;
}
double latitude;
double longitude;
};
// Periodically requests the IP-based geolocation and provides it to the
// observers, `GeolocationController::Observer`. This class also observes
// timezone changes to request a new geoposition.
// TODO(crbug.com/1272178): `GeolocationController` should observe the sleep
// and update next request time.
class ASH_EXPORT GeolocationController
: public SimpleGeolocationProvider::Observer,
public system::TimezoneSettings::Observer,
public chromeos::PowerManagerClient::Observer,
public SessionObserver {
public:
// Possible errors for `GetSunsetTime()` and `GetSunriseTime()`.
enum class SunRiseSetError {
// The current geolocation has no sunrise/sunset (24 hours of daylight or
// darkness).
kNoSunRiseSet,
// Sunrise/set are temporarily unavailable, including the default values of
// 6 AM/PM local time. Caller should handle this gracefully and try again
// later.
kUnavailable
};
static constexpr base::expected<base::Time, SunRiseSetError> kNoSunRiseSet =
base::unexpected(SunRiseSetError::kNoSunRiseSet);
static constexpr base::expected<base::Time, SunRiseSetError>
kSunRiseSetUnavailable = base::unexpected(SunRiseSetError::kUnavailable);
class Observer : public base::CheckedObserver {
public:
// Emitted when the Geoposition is updated with
// |possible_change_in_timezone| to indicate whether timezone might have
// changed as a result of the geoposition change.
virtual void OnGeopositionChanged(bool possible_change_in_timezone) {}
protected:
~Observer() override = default;
};
explicit GeolocationController(SimpleGeolocationProvider* const provider);
GeolocationController(const GeolocationController&) = delete;
GeolocationController& operator=(const GeolocationController&) = delete;
~GeolocationController() override;
static GeolocationController* Get();
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
const base::OneShotTimer& timer() const { return *timer_; }
const std::u16string& current_timezone_id() const {
return current_timezone_id_;
}
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// SimpleGeolocationProvider::Observer:
void OnGeolocationPermissionChanged(bool enabled) override;
// system::TimezoneSettings::Observer:
void TimezoneChanged(const icu::TimeZone& timezone) override;
// chromeos::PowerManagerClient::Observer:
void SuspendDone(base::TimeDelta sleep_duration) override;
// SessionObserver:
void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
// Returns sunset and sunrise time calculated from the most recently observed
// geoposition. If a geoposition has not been observed, defaults to sunset
// 6 PM and sunrise 6 AM.
base::expected<base::Time, SunRiseSetError> GetSunsetTime() const {
return GetSunRiseSet(/*sunrise=*/false);
}
base::expected<base::Time, SunRiseSetError> GetSunriseTime() const {
return GetSunRiseSet(/*sunrise=*/true);
}
static base::TimeDelta GetNextRequestDelayAfterSuccessForTesting();
base::OneShotTimer* GetTimerForTesting() { return timer_.get(); }
bool HasObserver(const Observer* obs) const {
return observers_.HasObserver(obs);
}
void SetTimerForTesting(std::unique_ptr<base::OneShotTimer> timer);
void SetClockForTesting(base::Clock* clock);
void SetLocalTimeConverterForTesting(
const LocalTimeConverter* local_time_converter);
void SetCurrentTimezoneIdForTesting(const std::u16string& timezone_id);
// Resets the running `timer_` and issues an immediate geoposition request.
// Any responses on the fly will be processed first, but will be overridden
// once the response of this request arrives.
void RequestImmediateGeopositionForTesting();
protected:
// The callback of geolocation request via `provider_`. Once receiving a
// new position, it `NotifyWithCurrentGeoposition()` to broadcast the position
// to observers and `ScheduleNextRequest()` on the next day. If the retrieval
// fails, it `ScheduleNextRequest()` after a `backoff_delay_`, which is
// doubled for each failure.
void OnGeoposition(const Geoposition& position,
bool server_error,
const base::TimeDelta elapsed);
// Virtual so that it can be overridden by a fake implementation in unit tests
// that doesn't request actual geopositions.
virtual void RequestGeoposition();
private:
// Calls `RequestGeoposition()` after `delay`.
void ScheduleNextRequest(base::TimeDelta delay);
// Broadcasts the change in geoposition to all observers with
// |possible_change_in_timezone| to indicate whether timezone might have
// changed as a result of the geoposition change.
void NotifyGeopositionChange(bool possible_change_in_timezone);
// Note that the below computation is intentionally performed every time
// GetSunsetTime() or GetSunriseTime() is called rather than once whenever we
// receive a geoposition (which happens at least once a day). This reduces
// the chances of getting inaccurate values, especially around DST changes.
base::expected<base::Time, SunRiseSetError> GetSunRiseSet(bool sunrise) const;
// Called only when the active user changes in order to see if we need to use
// a previously cached geoposition value from the active user's prefs.
void LoadCachedGeopositionIfNeeded();
// Called whenever we receive a new geoposition update to cache it in all
// logged-in users' prefs so that it can be used later in the event of not
// being able to retrieve a valid geoposition.
void StoreCachedGeoposition() const;
// Points to the `SimpleGeolocationProvider::GetInstance()` throughout the
// object lifecycle. Overridden in unit tests.
raw_ptr<SimpleGeolocationProvider> geolocation_provider_ = nullptr;
// May be null if a user has not logged in yet.
raw_ptr<PrefService> active_user_pref_service_ = nullptr;
std::unique_ptr<PrefChangeRegistrar> registrar_;
// Delay after which a new request is retried after a failed one.
base::TimeDelta backoff_delay_;
std::unique_ptr<base::OneShotTimer> timer_;
// Optional Used in tests to override the time of "Now".
raw_ptr<base::Clock> clock_ = nullptr; // Not owned.
// Optional Used in tests to override all local time operations.
raw_ptr<const LocalTimeConverter> local_time_converter_ =
nullptr; // Not owned.
// The ID of the current timezone in the format similar to "America/Chicago".
std::u16string current_timezone_id_;
base::ObserverList<Observer> observers_;
// True if the current `geoposition_` is from a previously cached value in the
// user prefs of any of the users in the current session. It is reset to false
// once we receive a newly-updated geoposition. This is used to treat the
// current geoposition as temporary until we receive a valid geoposition
// update, and also not to let a cached geoposition value to leak to another
// user for privacy reasons.
bool is_current_geoposition_from_cache_ = false;
std::unique_ptr<SimpleGeoposition> geoposition_;
ScopedSessionObserver scoped_session_observer_;
base::WeakPtrFactory<GeolocationController> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_SYSTEM_GEOLOCATION_GEOLOCATION_CONTROLLER_H_
|