chromium/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h

// 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 CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_INPUT_METRICS_H_
#define CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_INPUT_METRICS_H_

#include <map>

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
#include "chrome/browser/apps/app_service/metrics/browser_to_tab_list.h"
#include "chrome/browser/profiles/profile.h"
#include "components/services/app_service/public/cpp/instance_registry.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/events/event_handler.h"

namespace apps {

class InstanceUpdate;

// This is used for logging, so do not remove or reorder existing entries.
enum class InputEventSource {
  kUnknown = 0,
  kMouse = 1,
  kStylus = 2,
  kTouch = 3,
  kKeyboard = 4,

  // Add any new values above this one, and update kMaxValue to the highest
  // enumerator value.
  kMaxValue = kKeyboard,
};

extern const char kAppInputEventsKey[];

// This class is used to record the input events for the app windows.
class AppPlatformInputMetrics : public ui::EventHandler,
                                public InstanceRegistry::Observer,
                                public ukm::UkmRecorder::Observer {
 public:
  // For web apps and Chrome apps, there might be different app type name for
  // opening in tab or window. So record the app type name for the event count.
  using CountPerAppType = base::flat_map<AppTypeName, int>;

  // The map to record the event count for each InputEventSource.
  using EventSourceToCounts = base::flat_map<InputEventSource, CountPerAppType>;

  AppPlatformInputMetrics(Profile* profile,
                          const apps::AppRegistryCache& app_registry_cache,
                          InstanceRegistry& instance_registry);

  AppPlatformInputMetrics(const AppPlatformInputMetrics&) = delete;
  AppPlatformInputMetrics& operator=(const AppPlatformInputMetrics&) = delete;

  ~AppPlatformInputMetrics() override;

  // ui::EventHandler:
  void OnMouseEvent(ui::MouseEvent* event) override;
  void OnKeyEvent(ui::KeyEvent* event) override;
  void OnTouchEvent(ui::TouchEvent* event) override;

  void OnFiveMinutes();

  // Records the input events AppKM each 2 hours.
  void OnTwoHours();

 private:
  struct AppInfo {
    std::string app_id;
    AppTypeName app_type_name;
  };

  // InstanceRegistry::Observer:
  void OnInstanceUpdate(const InstanceUpdate& update) override;
  void OnInstanceRegistryWillBeDestroyed(InstanceRegistry* cache) override;

  // ukm::UkmRecorder::Observer:
  // Called only in Managed Guest Session since the observation is started only
  // in Managed Guest Session.
  void OnStartingShutdown() override;

  void SetAppInfoForActivatedWindow(AppType app_type,
                                    const std::string& app_id,
                                    aura::Window* window,
                                    const base::UnguessableToken& instance_id);

  void SetAppInfoForInactivatedWindow(
      const base::UnguessableToken& instance_id);

  void RecordEventCount(InputEventSource event_source,
                        ui::EventTarget* event_target);

  ukm::SourceId GetSourceId(const std::string& app_id);

  void RecordInputEventsAppKM();

  void RecordInputEventsAppKMForApp(const std::string& app_id,
                                    const EventSourceToCounts& event_counts);

  // Saves the input events in `app_id_to_event_count_per_two_hours_` to the
  // user pref each 2 hours. For example:
  // web_app_id1: {
  //   mouse:    { ChromeBrowser: 5, WebApp: 2}
  //   keyboard: { ChromeBrowser: 2, WebApp: 3}
  // },
  // chrome_app_id2: {
  //   stylus:   { ChromeBrowser: 2, ChromeApp: 12}
  //   keyboard: { ChromeBrowser: 3, ChromeApp: 30}
  // },
  // Arc_app_id3: {
  //   mouse:   { Arc: 5}
  // },
  void SaveInputEvents();

  // Records the input events AppKM saved in the user pref.
  void RecordInputEventsAppKMFromPref();

  // Returns true if recording is allowed for this app.
  bool ShouldRecordAppKMForApp(const std::string& app_id);

  raw_ptr<Profile> profile_;

  const raw_ref<const AppRegistryCache> app_registry_cache_;

  BrowserToTabList browser_to_tab_list_;

  bool should_record_ukm_from_pref_ = true;

  // The map from the window to the app info.
  base::flat_map<aura::Window*, AppInfo> window_to_app_info_;

  // Records the input even count for each app id in the past five minutes. Each
  // app id might have multiple events. For web apps and Chrome apps, there
  // might be different app type name, e.g. Chrome browser for apps opening in
  // a tab, or Web app for apps opening in a window. For example:
  // web_app_id1: {
  //   mouse:    { Chrome browser: 5, Web app: 2}
  //   Keyboard: { Chrome browser: 2, Web app: 3}
  // },
  // chrome_app_id2: {
  //   stylus:   { Chrome browser: 2, Chrome app: 12}
  //   Keyboard: { Chrome browser: 3, Chrome app: 30}
  // },
  // Arc_app_id3: {
  //   mouse:   { Arc: 5}
  // },
  std::map<std::string, EventSourceToCounts>
      app_id_to_event_count_per_two_hours_;

  base::ScopedObservation<InstanceRegistry, InstanceRegistry::Observer>
      instance_registry_observation_{this};

  // Observes `UkmRecorder` only in Managed Guest Session.
  base::ScopedObservation<ukm::UkmRecorder, ukm::UkmRecorder::Observer>
      ukm_recorder_observer_{this};
};

}  // namespace apps

#endif  // CHROME_BROWSER_APPS_APP_SERVICE_METRICS_APP_PLATFORM_INPUT_METRICS_H_