1 // Copyright 2018 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 #ifndef CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_MODELLER_IMPL_H_
6 #define CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_MODELLER_IMPL_H_
7 
8 #include <memory>
9 
10 #include "base/macros.h"
11 #include "base/memory/scoped_refptr.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/optional.h"
14 #include "base/scoped_observer.h"
15 #include "base/sequence_checker.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/time/time.h"
19 #include "base/timer/timer.h"
20 #include "chrome/browser/chromeos/power/auto_screen_brightness/als_reader.h"
21 #include "chrome/browser/chromeos/power/auto_screen_brightness/als_samples.h"
22 #include "chrome/browser/chromeos/power/auto_screen_brightness/brightness_monitor.h"
23 #include "chrome/browser/chromeos/power/auto_screen_brightness/gaussian_trainer.h"
24 #include "chrome/browser/chromeos/power/auto_screen_brightness/model_config.h"
25 #include "chrome/browser/chromeos/power/auto_screen_brightness/model_config_loader.h"
26 #include "chrome/browser/chromeos/power/auto_screen_brightness/modeller.h"
27 #include "chrome/browser/chromeos/power/auto_screen_brightness/utils.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "ui/base/user_activity/user_activity_detector.h"
30 #include "ui/base/user_activity/user_activity_observer.h"
31 
32 namespace chromeos {
33 namespace power {
34 namespace auto_screen_brightness {
35 
36 struct Model {
37   Model();
38   Model(const base::Optional<MonotoneCubicSpline>& global_curve,
39         const base::Optional<MonotoneCubicSpline>& personal_curve,
40         int iteration_count);
41   Model(const Model& model);
42   ~Model();
43   base::Optional<MonotoneCubicSpline> global_curve;
44   base::Optional<MonotoneCubicSpline> personal_curve;
45   int iteration_count = 0;
46 };
47 
48 // Real implementation of Modeller.
49 // It monitors user-requested brightness changes, ambient light values and
50 // trains personal brightness curves when user remains idle for a period of
51 // time.
52 // An object of this class must be used on the same thread that created this
53 // object.
54 class ModellerImpl : public Modeller,
55                      public AlsReader::Observer,
56                      public BrightnessMonitor::Observer,
57                      public ModelConfigLoader::Observer,
58                      public ui::UserActivityObserver {
59  public:
60   static constexpr char kModelDir[] = "autobrightness";
61   static constexpr char kGlobalCurveFileName[] = "global_curve";
62   static constexpr char kPersonalCurveFileName[] = "personal_curve";
63   static constexpr char kModelIterationCountFileName[] = "iteration_count";
64 
65   // Global curve, personal curve and training iteration count will be saved to
66   // the file paths below.
67   struct ModelSavingSpec {
68     base::FilePath global_curve;
69     base::FilePath personal_curve;
70     base::FilePath iteration_count;
71   };
72 
73   // ModellerImpl has weak dependencies on all parameters except |trainer|.
74   ModellerImpl(const Profile* profile,
75                AlsReader* als_reader,
76                BrightnessMonitor* brightness_monitor,
77                ModelConfigLoader* model_config_loader,
78                ui::UserActivityDetector* user_activity_detector,
79                std::unique_ptr<Trainer> trainer);
80   ~ModellerImpl() override;
81 
82   // Modeller overrides:
83   void AddObserver(Modeller::Observer* observer) override;
84   void RemoveObserver(Modeller::Observer* observer) override;
85 
86   // AlsReader::Observer overrides:
87   void OnAmbientLightUpdated(int lux) override;
88   void OnAlsReaderInitialized(AlsReader::AlsInitStatus status) override;
89 
90   // BrightnessMonitor::Observer overrides:
91   void OnBrightnessMonitorInitialized(bool success) override;
92   void OnUserBrightnessChanged(double old_brightness_percent,
93                                double new_brightness_percent) override;
94   void OnUserBrightnessChangeRequested() override;
95 
96   // ModelConfigLoader::Observer overrides:
97   void OnModelConfigLoaded(base::Optional<ModelConfig> model_config) override;
98 
99   // ui::UserActivityObserver overrides:
100   void OnUserActivity(const ui::Event* event) override;
101 
102   // ModellerImpl has weak dependencies on all parameters except |trainer|.
103   static std::unique_ptr<ModellerImpl> CreateForTesting(
104       const Profile* profile,
105       AlsReader* als_reader,
106       BrightnessMonitor* brightness_monitor,
107       ModelConfigLoader* model_config_loader,
108       ui::UserActivityDetector* user_activity_detector,
109       std::unique_ptr<Trainer> trainer,
110       scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
111       const base::TickClock* tick_clock);
112 
113   // Current average log ambient light.
114   base::Optional<double> AverageAmbientForTesting(base::TimeTicks now);
115 
116   // Current number of training data points stored, which will be used for next
117   // training.
118   size_t NumberTrainingDataPointsForTesting() const;
119 
120   // Returns |max_training_data_points_| for unit tests.
121   size_t GetMaxTrainingDataPointsForTesting() const;
122 
123   base::TimeDelta GetTrainingDelayForTesting() const;
124 
125   ModelConfig GetModelConfigForTesting() const;
126 
127   // Returns ModelSavingSpec used to store models. It also creates intermediate
128   // directories if they do not exist. The returned paths will be empty on
129   // failures.
130   static ModelSavingSpec GetModelSavingSpecFromProfilePath(
131       const base::FilePath& profile_path);
132 
133  private:
134   // ModellerImpl has weak dependencies on all parameters except |trainer|.
135   ModellerImpl(const Profile* profile,
136                AlsReader* als_reader,
137                BrightnessMonitor* brightness_monitor,
138                ModelConfigLoader* model_config_loader,
139                ui::UserActivityDetector* user_activity_detector,
140                std::unique_ptr<Trainer> trainer,
141                scoped_refptr<base::SequencedTaskRunner> task_runner,
142                const base::TickClock* tick_clock,
143                bool is_testing = false);
144 
145   // Called after we've attempted to read model saving spec from the user
146   // profile.
147   void OnModelSavingSpecReadFromProfile(const ModelSavingSpec& spec);
148 
149   // Called to handle a status change in one of the dependencies (ALS,
150   // brightness monitor, model config loader) of the modeller. If all
151   // dependencies are successfully initialized, attempts initialization of
152   // the modeller (curve loading, parameter customization) and notifies
153   // observers about the result.
154   void HandleStatusUpdate();
155 
156   // Applies customizations from model configs. Returns whether it is
157   // successful.
158   bool ApplyCustomization();
159 
160   // Called as soon as |is_modeller_enabled_| has its value set. It will notify
161   // all observers.
162   void OnInitializationComplete();
163 
164   // Notifies a given observer about the state of the modeller. Will provide
165   // either
166   // - no curves (if modeller is disabled),
167   // - just a global curve (if no personal curve is available), or
168   // - both a global and personal curve.
169   void NotifyObserverInitStatus(Modeller::Observer& observer);
170 
171   // Sets the global and personal curves based on the model read from disk. If
172   // the model is invalid or not based on the current model config, instead
173   // resets the global and personal curves.
174   void OnModelLoadedFromDisk(const Model& model);
175 
176   void OnModelSavedToDisk(bool is_successful);
177 
178   // Called after we've set trainer's initial curves.
179   void OnSetInitialCurves(bool is_personal_curve_valid);
180 
181   // Either starts training immediately or delays it for |training_delay_|.
182   // Training starts immediately if |training_delay_| is 0 or number of training
183   // points reached |max_training_data_points_|.
184   // This function is called after a user brightness change signal is received
185   // (that will be used as an example), and when a user activity is detected.
186   // It's also called after initial curves are set.
187   // Nothing will happen if model is not enabled.
188   void ScheduleTrainerStart();
189 
190   // Starts model training and runs it in non UI thread. Also clears
191   // |data_cache_|.
192   void StartTraining();
193 
194   // Called after training is complete.
195   void OnTrainingFinished(const TrainingResult& result);
196 
197   // Erase all info related to the personal curve.
198   void ErasePersonalCurve();
199 
200   // If |is_testing_| is false, we check curve saving/loading and training jobs
201   // are running on non-UI thread.
202   const bool is_testing_ = false;
203 
204   // If number of recorded training data has reached |max_training_data_points_|
205   // we start training immediately, without waiting for user to become idle for
206   // |training_delay_|. This can be overridden by experiment flag
207   // "max_training_data_points".
208   size_t max_training_data_points_ = 100;
209 
210   // Once user remains idle for |training_delay_|, we start training the model.
211   // If this value is 0, we will not need to wait for user to remain inactive.
212   // This can be overridden by experiment flag "training_delay_in_seconds".
213   base::TimeDelta training_delay_ = base::TimeDelta::FromSeconds(0);
214 
215   // If personal curve error is above this threshold, the curve will not be
216   // exported. The error is expressed in terms of percentages.
217   double curve_error_tolerance_ = 5.0;
218 
219   ScopedObserver<AlsReader, AlsReader::Observer> als_reader_observer_;
220 
221   ScopedObserver<BrightnessMonitor, BrightnessMonitor::Observer>
222       brightness_monitor_observer_;
223 
224   ScopedObserver<ModelConfigLoader, ModelConfigLoader::Observer>
225       model_config_loader_observer_;
226 
227   ScopedObserver<ui::UserActivityDetector, ui::UserActivityObserver>
228       user_activity_observer_;
229 
230   // Background task runner for IO work (loading a curve from disk and writing a
231   // curve to disk) and training jobs.
232   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
233   std::unique_ptr<Trainer, base::OnTaskRunnerDeleter> trainer_;
234 
235   // This will be replaced by a mock tick clock during tests.
236   const base::TickClock* const tick_clock_;
237 
238   base::OneShotTimer model_timer_;
239 
240   base::Optional<AlsReader::AlsInitStatus> als_init_status_;
241   base::Optional<bool> brightness_monitor_success_;
242 
243   // |model_config_exists_| will remain nullopt until |OnModelConfigLoaded| is
244   // called. Its value will then be set to true if the input model config exists
245   // (not nullopt), else its value will be false.
246   base::Optional<bool> model_config_exists_;
247   ModelConfig model_config_;
248 
249   // Whether this modeller has initialized successfully, including connecting
250   // to AlsReader, BrightnessMonitor and loading a Trainer.
251   // Initially has no value. Guaranteed to have a value after the completion of
252   // |OnModelLoadedFromDisk|.
253   base::Optional<bool> is_modeller_enabled_;
254 
255   base::Optional<ModelSavingSpec> model_saving_spec_;
256 
257   // Whether the initial global curve is reset to the one constructed from
258   // model config. It is true if there is no saved model loaded from the disk
259   // or if the saved global curve is different from the curve from model config.
260   // If this flag is true, then the global curve is saved to the disk the first
261   // time a personal curve is trained and saved to disk; it will be set to false
262   // after the first saving is done.
263   bool global_curve_reset_ = false;
264 
265   // |model_| will be set after initialization is complete and updated each time
266   // training is done with a new curve.
267   Model model_;
268 
269   // |initial_global_curve_| is constructed from model config.
270   base::Optional<MonotoneCubicSpline> initial_global_curve_;
271 
272   // Recent log ambient values.
273   std::unique_ptr<AmbientLightSampleBuffer> log_als_values_;
274 
275   std::vector<TrainingDataPoint> data_cache_;
276 
277   base::ObserverList<Modeller::Observer> observers_;
278 
279   // Training start time.
280   base::Optional<base::TimeTicks> training_start_;
281 
282   SEQUENCE_CHECKER(sequence_checker_);
283 
284   base::WeakPtrFactory<ModellerImpl> weak_ptr_factory_{this};
285 
286   DISALLOW_COPY_AND_ASSIGN(ModellerImpl);
287 };
288 
289 // Saves |model| to disk at location specified by |model_saving_spec| and
290 // returns whether it was successful. This should run in another thread to be
291 // non-blocking to the main thread (if |is_testing| is false).
292 // Not every components of |model| will be saved:
293 // 1. |global_curve| is saved only if |save_global_curve| is true.
294 // 2. |personal_curve| is saved only if |save_personal_curve| is true.
295 // 3. |iteration_count| is always saved.
296 bool SaveModelToDisk(const ModellerImpl::ModelSavingSpec& model_saving_spec,
297                      const Model& model,
298                      bool save_global_curve,
299                      bool save_personal_curve,
300                      bool is_testing);
301 
302 }  // namespace auto_screen_brightness
303 }  // namespace power
304 }  // namespace chromeos
305 
306 #endif  // CHROME_BROWSER_CHROMEOS_POWER_AUTO_SCREEN_BRIGHTNESS_MODELLER_IMPL_H_
307