File: geolocation_controller.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (228 lines) | stat: -rw-r--r-- 9,064 bytes parent folder | download | duplicates (6)
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_