chromium/ios/chrome/browser/omaha/model/omaha_service.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef IOS_CHROME_BROWSER_OMAHA_MODEL_OMAHA_SERVICE_H_
#define IOS_CHROME_BROWSER_OMAHA_MODEL_OMAHA_SERVICE_H_

#include <memory>
#include <string>

#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/no_destructor.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/values.h"
#include "base/version.h"
#include "ios/chrome/browser/upgrade/model/upgrade_recommended_details.h"

class OmahaService;

namespace network {
class SharedURLLoaderFactory;
class PendingSharedURLLoaderFactory;
class SimpleURLLoader;
}  // namespace network

struct UpgradeRecommendedDetails;

// All `OmahaServiceObserver` events will be evaluated on the same sequence the
// `OmahaService` is created on.
class OmahaServiceObserver : public base::CheckedObserver {
 public:
  // Called whenever the Omaha Service determines a change in
  // `UpgradeRecommendedDetails`.
  virtual void UpgradeRecommendedDetailsChanged(
      UpgradeRecommendedDetails details) {}

  // Notifies the observer that `omaha_service` has begun shutting down.
  // Observers should remove themselves from the service via
  // `omaha_service->RemoveObserver(...)` when this happens.
  virtual void ServiceWillShutdown(OmahaService* omaha_service) {}
};

// This service handles the communication with the Omaha server. It also
// handles all the scheduling necessary to contact the server regularly.
// All methods, but the constructor, `GetInstance` and `Start` methods, must be
// called from the IO thread.
class OmahaService {
 public:
  // Called when an upgrade is recommended.
  using UpgradeRecommendedCallback =
      base::RepeatingCallback<void(const UpgradeRecommendedDetails&)>;

  // Called when a one-off Omaha check returns.
  using OneOffCallback = base::OnceCallback<void(UpgradeRecommendedDetails)>;

  // Starts the service. Also set the `URLLoaderFactory` necessary to access the
  // Omaha server. This method should only be called once.  Does nothing if
  // Omaha should not be enabled for this build variant.
  static void Start(std::unique_ptr<network::PendingSharedURLLoaderFactory>
                        pending_url_loader_factory,
                    const UpgradeRecommendedCallback& callback);

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

  // Posts to CheckNowOnIOThread on IO thread to perform an immediate check
  // if the device is up to date.
  static void CheckNow(OneOffCallback callback);

  // Adds/removes an observer to be notified of `OmahaServiceObserver` events.
  static void AddObserver(OmahaServiceObserver* observer);
  static void RemoveObserver(OmahaServiceObserver* observer);
  void RegisterObserver(OmahaServiceObserver* observer);
  void UnregisterObserver(OmahaServiceObserver* observer);

  // Returns debug information about the omaha service.
  static void GetDebugInformation(
      base::OnceCallback<void(base::Value::Dict)> callback);

 private:
  // For tests:
  friend class OmahaServiceTest;
  friend class OmahaServiceInternalTest;
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, PingMessageTest);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest,
                           PingMessageTestWithUnknownInstallDate);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, InstallEventMessageTest);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, SendPingFailure);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, SendPingSuccess);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest,
                           CallbackForScheduledNotUsedOnErrorResponse);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, OneOffSuccess);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, OngoingPingOneOffCallbackUsed);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, OneOffCallbackUsedOnlyOnce);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, ScheduledPingDuringOneOffDropped);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, ParseAndEchoLastServerDate);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, SendInstallEventSuccess);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, SendPingReceiveUpdate);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, PersistStatesTest);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, BackoffTest);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, NonSpammingTest);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, ActivePingAfterInstallEventTest);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, InstallRetryTest);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, PingUpToDateUpdatesUserDefaults);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceTest, PingOutOfDateUpdatesUserDefaults);
  FRIEND_TEST_ALL_PREFIXES(OmahaServiceInternalTest,
                           PingMessageTestWithProfileData);

  // For the singleton:
  friend class base::NoDestructor<OmahaService>;

  // Enum for the `GetPingContent` and `GetNextPingRequestId` method.
  enum PingContent {
    INSTALL_EVENT,
    USAGE_PING,
  };

  // Starts the service. Called on startup. `task_runner` ensures responses from
  // async Omaha requests are posted on the same sequence that `OmahaService`
  // was created on.
  void StartInternal(
      const scoped_refptr<base::SequencedTaskRunner> task_runner);

  // Stops the service in preparation for browser shutdown.
  void StopInternal();

  // URL loader completion callback.
  void OnURLLoadComplete(std::unique_ptr<std::string> response_body);

  // Returns whether Omaha is enabled for this build variant.
  static bool IsEnabled();

  // Raw `GetInstance` method. Necessary for using singletons. This method must
  // only be called if `IsEnabled()` returns true.
  static OmahaService* GetInstance();

  // Private constructor, only used by the singleton.
  OmahaService();
  // Private constructor, only used for tests.
  explicit OmahaService(bool schedule);
  ~OmahaService();

  // Returns the time to wait before next attempt.
  static base::TimeDelta GetBackOff(uint8_t number_of_tries);

  void set_upgrade_recommended_callback(
      const UpgradeRecommendedCallback& callback) {
    upgrade_recommended_callback_ = callback;
  }

  // Notifies all observers of the latest `details`.
  void NotifyObservers(UpgradeRecommendedDetails details);

  // Sends a ping to the Omaha server.
  void SendPing();

  // Method that will either start sending a ping to the server, or schedule
  // itself to be called again when the next ping must be send.
  void SendOrScheduleNextPing();

  // Persists the state of the service.
  void PersistStates();

  // Returns the XML representation of the ping message to send to the Omaha
  // server. If `sendInstallEvent` is true, the message will contain an
  // installation complete event.
  std::string GetPingContent(const std::string& requestId,
                             const std::string& sessionId,
                             const std::string& versionName,
                             const std::string& channelName,
                             const base::Time& installationTime,
                             PingContent pingContent);

  // Returns the xml representation of the ping message to send to the Omaha
  // server. Use the current state of the service to compute the right message.
  std::string GetCurrentPingContent();

  // Performs an immediate check to see if the device is up to date. Start must
  // have been previously called.
  void CheckNowOnIOThread(OneOffCallback callback);

  // Computes debugging information and fill `result`.
  void GetDebugInformationOnIOThread(
      base::OnceCallback<void(base::Value::Dict)> callback);

  // Returns whether the next ping to send must a an install/update ping. If
  // `true`, the next ping must use `GetInstallRetryRequestId` as identifier
  // for the request and must include a X-RequestAge header.
  bool IsNextPingInstallRetry();

  // Returns the request identifier to use for the next ping. If it is an
  // install/update retry, it will return the identifier used on the initial
  // request. If this is not the case, returns a random id.
  // `send_install_event` must be true if the next ping is a install/update
  // event, in that case, the identifier will be stored so that it can be
  // reused until the ping is successful.
  std::string GetNextPingRequestId(PingContent ping_content);

  // Stores the given request id to be reused on install/update retry.
  void SetInstallRetryRequestId(const std::string& request_id);

  // Clears the stored request id for a installation/update ping retry. Must be
  // called after a successful installation/update ping.
  void ClearInstallRetryRequestId();

  // Initialize the URLLoaderFactory instance (mostly needed for tests).
  void InitializeURLLoaderFactory(
      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);

  // Clears the all persistent state. Should only be used for testing.
  static void ClearPersistentStateForTests();

  // To communicate with the Omaha server.
  std::unique_ptr<network::SimpleURLLoader> url_loader_;
  std::unique_ptr<network::PendingSharedURLLoaderFactory>
      pending_url_loader_factory_;
  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;

  // Whether the service has been started.
  bool started_;

  // The timer that call this object back when needed.
  base::OneShotTimer timer_;

  // Whether to schedule pings. This is only false for tests.
  const bool schedule_;

  // The install date of the application.  This is fetched in `StartInternal` on
  // the main thread and cached for use on the IO thread.
  int64_t application_install_date_;

  // The time at which the last ping was sent.
  base::Time last_sent_time_;

  // The time at which to send the next ping.
  base::Time next_tries_time_;

  // The timestamp of the ping to send.
  base::Time current_ping_time_;

  // Last version for which an installation ping has been sent.
  base::Version last_sent_version_;

  // Last received server date.
  int last_server_date_;

  // The language in use at start up.
  std::string locale_lang_;

  // Number of tries of the last ping.
  uint8_t number_of_tries_;

  // Whether the ping currently being sent is an install (new or update) ping.
  bool sending_install_event_;

  // If a scheduled ping was canceled.
  bool scheduled_ping_canceled_ = false;

  // Called to notify that upgrade is recommended.
  UpgradeRecommendedCallback upgrade_recommended_callback_;

  // Stores the callback for one off Omaha checks.
  OneOffCallback one_off_check_callback_;

  // Observers to listen to `OmahaService` changes.
  base::ObserverList<OmahaServiceObserver, true> observers_;

  // Validates `OmahaServiceObserver` events are evaluated on the same sequence
  // that `OmahaService` was created on.
  SEQUENCE_CHECKER(sequence_checker_);

  // Ensures responses from async Omaha requests are posted on the same sequence
  // that `OmahaService` was created on.
  scoped_refptr<base::SequencedTaskRunner> task_runner_;
};

#endif  // IOS_CHROME_BROWSER_OMAHA_MODEL_OMAHA_SERVICE_H_