| // Copyright 2017 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_COMPONENT_UPDATER_CROS_COMPONENT_INSTALLER_CHROMEOS_H_ |
| #define CHROME_BROWSER_COMPONENT_UPDATER_CROS_COMPONENT_INSTALLER_CHROMEOS_H_ |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "components/component_updater/ash/component_manager_ash.h" |
| #include "components/component_updater/component_installer.h" |
| #include "components/component_updater/component_updater_service.h" |
| #include "components/update_client/update_client.h" |
| |
| namespace component_updater { |
| |
| // The name of the directory under DIR_COMPONENT_USER that cros component |
| // installers puts all of the installed components. |
| extern const char kComponentsRootPath[]; |
| |
| class ComponentUpdateService; |
| class MetadataTable; |
| class CrOSComponentInstaller; |
| |
| // Describes all metadata needed to dynamically install ChromeOS components. |
| struct ComponentConfig { |
| // This is a client-only identifier for the component. |
| const char* name; |
| // ComponentInstallerPolicy to use. |
| enum class PolicyType { |
| kEnvVersion, // Checks env_version, see below. |
| kDemoApp, // Adds demo-mode-specific install attributes |
| kGrowthCampaigns, // Adds growth campaigns install attributes |
| }; |
| PolicyType policy_type; |
| // This is used for ABI compatibility checks. It is compared against the |
| // 'min_env_version' key in the component's manifest.json file. It uses |
| // standard major.minor compat rules, where ABI is compatible if and only if |
| // major is matching. The client will send this string to the omaha server, |
| // which will filter for a compatible update. Likewise, the client will |
| // avoid registering a component if there is an ABI mismatch between the |
| // already downloaded component and the expected major version. Must be |
| // non-empty for PolicyType::kEnvVersion. |
| const char* env_version; |
| // This is the app-id of the component, converted from [a-p] hex to [0-f] hex. |
| const char* sha2hash; |
| }; |
| |
| // Base class for all Chrome OS components. |
| class CrOSComponentInstallerPolicy : public ComponentInstallerPolicy { |
| public: |
| CrOSComponentInstallerPolicy( |
| const ComponentConfig& config, |
| CrOSComponentInstaller* cros_component_installer); |
| |
| CrOSComponentInstallerPolicy(const CrOSComponentInstallerPolicy&) = delete; |
| CrOSComponentInstallerPolicy& operator=(const CrOSComponentInstallerPolicy&) = |
| delete; |
| |
| ~CrOSComponentInstallerPolicy() override; |
| |
| // ComponentInstallerPolicy: |
| bool SupportsGroupPolicyEnabledComponentUpdates() const override; |
| bool RequiresNetworkEncryption() const override; |
| update_client::CrxInstaller::Result OnCustomInstall( |
| const base::Value::Dict& manifest, |
| const base::FilePath& install_dir) override; |
| void OnCustomUninstall() override; |
| bool VerifyInstallation(const base::Value::Dict& manifest, |
| const base::FilePath& install_dir) const override; |
| base::FilePath GetRelativeInstallDir() const override; |
| void GetHash(std::vector<uint8_t>* hash) const override; |
| std::string GetName() const override; |
| bool AllowUpdates() const override; |
| |
| protected: |
| const raw_ptr<CrOSComponentInstaller, DanglingUntriaged> |
| cros_component_installer_; |
| |
| private: |
| const std::string name_; |
| std::vector<uint8_t> sha2_hash_; |
| }; |
| |
| // An installer policy that does ABI compatibility checks based on |
| // ComponentConfig::env_version, see above. |
| class EnvVersionInstallerPolicy : public CrOSComponentInstallerPolicy { |
| public: |
| EnvVersionInstallerPolicy(const ComponentConfig& config, |
| CrOSComponentInstaller* cros_component_installer); |
| EnvVersionInstallerPolicy(const EnvVersionInstallerPolicy&) = delete; |
| EnvVersionInstallerPolicy& operator=(const EnvVersionInstallerPolicy&) = |
| delete; |
| ~EnvVersionInstallerPolicy() override; |
| |
| // ComponentInstallerPolicy: |
| void ComponentReady(const base::Version& version, |
| const base::FilePath& path, |
| base::Value::Dict manifest) override; |
| update_client::InstallerAttributes GetInstallerAttributes() const override; |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, IsCompatibleOrNot); |
| |
| static bool IsCompatible(const std::string& env_version_str, |
| const std::string& min_env_version_str); |
| |
| const std::string env_version_; |
| }; |
| |
| // An installer policy for the ChromeOS Demo Mode app, which includes special |
| // system-sourced installer attributes in the request to receive customized |
| // app versions |
| class DemoAppInstallerPolicy : public CrOSComponentInstallerPolicy { |
| public: |
| DemoAppInstallerPolicy(const ComponentConfig& config, |
| CrOSComponentInstaller* cros_component_installer); |
| DemoAppInstallerPolicy(const DemoAppInstallerPolicy&) = delete; |
| DemoAppInstallerPolicy& operator=(const DemoAppInstallerPolicy&) = delete; |
| ~DemoAppInstallerPolicy() override; |
| |
| // ComponentInstallerPolicy: |
| void ComponentReady(const base::Version& version, |
| const base::FilePath& path, |
| base::Value::Dict manifest) override; |
| update_client::InstallerAttributes GetInstallerAttributes() const override; |
| }; |
| |
| // An installer policy for the ChromeOS growth campaigns, which includes special |
| // system-sourced installer attributes in the request to receive customized |
| // campaigns versions. |
| class GrowthCampaignsInstallerPolicy : public CrOSComponentInstallerPolicy { |
| public: |
| GrowthCampaignsInstallerPolicy( |
| const ComponentConfig& config, |
| CrOSComponentInstaller* cros_component_installer); |
| GrowthCampaignsInstallerPolicy(const GrowthCampaignsInstallerPolicy&) = |
| delete; |
| GrowthCampaignsInstallerPolicy& operator=( |
| const GrowthCampaignsInstallerPolicy&) = delete; |
| ~GrowthCampaignsInstallerPolicy() override; |
| |
| // ComponentInstallerPolicy: |
| void ComponentReady(const base::Version& version, |
| const base::FilePath& path, |
| base::Value::Dict manifest) override; |
| update_client::InstallerAttributes GetInstallerAttributes() const override; |
| }; |
| |
| // This class contains functions used to register and install a component. |
| class CrOSComponentInstaller : public ComponentManagerAsh { |
| public: |
| CrOSComponentInstaller(std::unique_ptr<MetadataTable> metadata_table, |
| ComponentUpdateService* component_updater); |
| |
| CrOSComponentInstaller(const CrOSComponentInstaller&) = delete; |
| CrOSComponentInstaller& operator=(const CrOSComponentInstaller&) = delete; |
| |
| // ComponentManagerAsh: |
| void SetDelegate(Delegate* delegate) override; |
| void Load(const std::string& name, |
| MountPolicy mount_policy, |
| UpdatePolicy update_policy, |
| LoadCallback load_callback) override; |
| bool Unload(const std::string& name) override; |
| void GetVersion(const std::string& name, |
| base::OnceCallback<void(const base::Version&)> |
| version_callback) const override; |
| void RegisterCompatiblePath(const std::string& name, |
| CompatibleComponentInfo info) override; |
| void RegisterInstalled() override; |
| |
| void UnregisterCompatiblePath(const std::string& name) override; |
| base::FilePath GetCompatiblePath(const std::string& name) const override; |
| bool IsRegisteredMayBlock(const std::string& name) override; |
| |
| // Called when a component is installed/updated. |
| // Broadcasts a D-Bus signal for a successful component installation. |
| void EmitInstalledSignal(const std::string& component); |
| |
| // The load cache contains three pieces of information: |
| // (1) For a given component, whether the load request was successful, a |
| // failure, or in-progress. |
| // (2) If the load request was successful, the file path to the loaded |
| // image. |
| // (3) If the load request is in progress, the callbacks to invoke after the |
| // load request finishes. |
| struct LoadInfo { |
| LoadInfo(); |
| ~LoadInfo(); |
| // If null, then the request is pending. |
| std::optional<bool> success; |
| // Only populated on success. |
| base::FilePath path; |
| // Only populated if request is pending. Includes all subsequent callbacks |
| // after the first. |
| std::vector<LoadCallback> callbacks; |
| }; |
| |
| // Removes the load cache entry for `component_name`. Currently this is done |
| // to avoid dispatching loads for old component versions. This can occur when |
| // the old version has loaded successfully and is now in the load cache. |
| void RemoveLoadCacheEntry(const std::string& component_name); |
| |
| // Test-only method for introspection. |
| std::map<std::string, LoadInfo>& GetLoadCacheForTesting(); |
| |
| protected: |
| ~CrOSComponentInstaller() override; |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, RegisterComponent); |
| FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, |
| BPPPCompatibleCrOSComponent); |
| FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, CompatibilityOK); |
| FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, |
| CompatibilityMissingManifest); |
| FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, IsCompatibleOrNot); |
| FRIEND_TEST_ALL_PREFIXES(CrOSComponentInstallerTest, CompatibleCrOSComponent); |
| |
| // Registers a component with a dedicated ComponentUpdateService instance. |
| void Register(const ComponentConfig& config, |
| base::OnceClosure register_callback); |
| |
| // Installs a component with a dedicated ComponentUpdateService instance. |
| void Install(const std::string& name, |
| UpdatePolicy update_policy, |
| MountPolicy mount_policy, |
| LoadCallback load_callback); |
| |
| // Calls OnDemandUpdate to install the component right after being registered. |
| // |id| is the component id generated from its sha2 hash. |
| void StartInstall(const std::string& name, |
| const std::string& id, |
| UpdatePolicy update_policy, |
| update_client::Callback install_callback); |
| |
| // Calls LoadInternal to load the installed component. |
| void FinishInstall(const std::string& name, |
| MountPolicy mount_policy, |
| UpdatePolicy update_policy, |
| LoadCallback load_callback, |
| update_client::Error error); |
| |
| // Internal function to load a component. |
| void LoadInternal(const std::string& name, LoadCallback load_callback); |
| |
| // Calls load_callback and pass in the parameter |result| (component mount |
| // point). |
| void FinishLoad(LoadCallback load_callback, |
| const std::string& name, |
| std::optional<base::FilePath> result); |
| |
| // Calls `version_callback` and pass in the parameter `result` (component |
| // version). |
| void FinishGetVersion( |
| base::OnceCallback<void(const base::Version&)> version_callback, |
| std::optional<std::string> result) const; |
| |
| // Registers component |configs| to be updated. |
| void RegisterN(const std::vector<ComponentConfig>& configs); |
| |
| // Checks if the current installed component is compatible given a component |
| // |name|. |
| bool IsCompatible(const std::string& name) const; |
| |
| // Posts a task with the response information for |callback|. |
| void DispatchLoadCallback(LoadCallback callback, |
| base::FilePath path, |
| bool success); |
| // Repeatedly calls DispatchLoadCallback with failure parameters. |
| void DispatchFailedLoads(std::vector<LoadCallback> callbacks); |
| |
| // Maps from a compatible component name to its info. |
| base::flat_map<std::string, CompatibleComponentInfo> compatible_components_; |
| |
| // A weak pointer to a Delegate for emitting D-Bus signal. |
| raw_ptr<Delegate> delegate_ = nullptr; |
| |
| // Table storing metadata (installs, usage, etc.). |
| std::unique_ptr<MetadataTable> metadata_table_; |
| |
| // The load cache stores ongoing load requests, as well as the finished |
| // results. |
| std::map<std::string, LoadInfo> load_cache_; |
| |
| const raw_ptr<ComponentUpdateService> component_updater_; |
| |
| base::WeakPtrFactory<CrOSComponentInstaller> weak_factory_{this}; |
| }; |
| |
| } // namespace component_updater |
| |
| #endif // CHROME_BROWSER_COMPONENT_UPDATER_CROS_COMPONENT_INSTALLER_CHROMEOS_H_ |