1 // Copyright 2020 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 #include "chrome/browser/component_updater/smart_dim_component_installer.h"
6
7 #include <cstddef>
8 #include <tuple>
9
10 #include "base/bind.h"
11 #include "base/feature_list.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/metrics/field_trial_params.h"
17 #include "base/optional.h"
18 #include "base/task/post_task.h"
19 #include "base/task/thread_pool.h"
20 #include "base/version.h"
21 #include "chrome/browser/chromeos/power/ml/smart_dim/metrics.h"
22 #include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h"
23 #include "chromeos/constants/chromeos_features.h"
24 #include "components/component_updater/component_updater_service.h"
25 #include "content/public/browser/browser_thread.h"
26
27 namespace {
28
29 using ::chromeos::power::ml::ComponentFileContents;
30 using ::chromeos::power::ml::ComponentVersionType;
31 using ::chromeos::power::ml::LoadComponentEvent;
32 using ::chromeos::power::ml::LogComponentVersionType;
33 using ::chromeos::power::ml::LogLoadComponentEvent;
34
35 const base::FilePath::CharType kSmartDimFeaturePreprocessorConfigFileName[] =
36 FILE_PATH_LITERAL("example_preprocessor_config.pb");
37 const base::FilePath::CharType kSmartDimModelFileName[] =
38 FILE_PATH_LITERAL("mlservice-model-smart_dim.tflite");
39 const base::FilePath::CharType kSmartDimMetaJsonFileName[] =
40 FILE_PATH_LITERAL("smart_dim_meta.json");
41
42 const char kDefaultVersion[] = "20200601.0";
43
44 constexpr base::FeatureParam<std::string> kVersion{
45 &chromeos::features::kSmartDimExperimentalComponent,
46 "smart_dim_experimental_version", kDefaultVersion};
47
48 // The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
49 // The extension id is: ghiclnejioiofblmbphpgbhaojnkempa
50 const uint8_t kSmartDimPublicKeySHA256[32] = {
51 0x67, 0x82, 0xbd, 0x49, 0x8e, 0x8e, 0x51, 0xbc, 0x1f, 0x7f, 0x61,
52 0x70, 0xe9, 0xda, 0x4c, 0xf0, 0x30, 0x2e, 0x24, 0x4d, 0x68, 0x17,
53 0x19, 0xad, 0x26, 0x6e, 0xd0, 0x33, 0x03, 0xb3, 0xe5, 0xff};
54
55 const char kMLSmartDimManifestName[] = "Smart Dim";
56
57
58 // Read files from the component to strings, should be called from a blocking
59 // task runner.
ReadComponentFiles(const base::FilePath & meta_json_path,const base::FilePath & preprocessor_pb_path,const base::FilePath & model_path)60 base::Optional<ComponentFileContents> ReadComponentFiles(
61 const base::FilePath& meta_json_path,
62 const base::FilePath& preprocessor_pb_path,
63 const base::FilePath& model_path) {
64 std::string metadata_json, preprocessor_proto, model_flatbuffer;
65 if (!base::ReadFileToString(meta_json_path, &metadata_json) ||
66 !base::ReadFileToString(preprocessor_pb_path, &preprocessor_proto) ||
67 !base::ReadFileToString(model_path, &model_flatbuffer)) {
68 DLOG(ERROR) << "Failed reading component files.";
69 return base::nullopt;
70 }
71
72 return std::make_tuple(std::move(metadata_json),
73 std::move(preprocessor_proto),
74 std::move(model_flatbuffer));
75 }
76
UpdateSmartDimMlAgent(const base::Optional<ComponentFileContents> & result)77 void UpdateSmartDimMlAgent(
78 const base::Optional<ComponentFileContents>& result) {
79 if (result == base::nullopt) {
80 LogLoadComponentEvent(LoadComponentEvent::kReadComponentFilesError);
81 return;
82 }
83
84 chromeos::power::ml::SmartDimMlAgent::GetInstance()->OnComponentReady(
85 result.value());
86 }
87
88 } // namespace
89
90 namespace component_updater {
91
SmartDimComponentInstallerPolicy(std::string expected_version)92 SmartDimComponentInstallerPolicy::SmartDimComponentInstallerPolicy(
93 std::string expected_version)
94 : expected_version_(expected_version) {}
95
96 SmartDimComponentInstallerPolicy::~SmartDimComponentInstallerPolicy() = default;
97
98 bool SmartDimComponentInstallerPolicy::
SupportsGroupPolicyEnabledComponentUpdates() const99 SupportsGroupPolicyEnabledComponentUpdates() const {
100 return false;
101 }
102
RequiresNetworkEncryption() const103 bool SmartDimComponentInstallerPolicy::RequiresNetworkEncryption() const {
104 return false;
105 }
106
107 update_client::CrxInstaller::Result
OnCustomInstall(const base::DictionaryValue & manifest,const base::FilePath & install_dir)108 SmartDimComponentInstallerPolicy::OnCustomInstall(
109 const base::DictionaryValue& manifest,
110 const base::FilePath& install_dir) {
111 return update_client::CrxInstaller::Result(0); // Nothing custom here.
112 }
113
OnCustomUninstall()114 void SmartDimComponentInstallerPolicy::OnCustomUninstall() {}
115
ComponentReady(const base::Version & version,const base::FilePath & install_dir,std::unique_ptr<base::DictionaryValue> manifest)116 void SmartDimComponentInstallerPolicy::ComponentReady(
117 const base::Version& version,
118 const base::FilePath& install_dir,
119 std::unique_ptr<base::DictionaryValue> manifest) {
120 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
121 // If IsDownloadWorkerReady(), newly downloaded components will take effect
122 // on next reboot. This makes sure the updating happens at most once.
123 if (chromeos::power::ml::SmartDimMlAgent::GetInstance()
124 ->IsDownloadWorkerReady()) {
125 DVLOG(1) << "Download_worker in SmartDimMlAgent is ready, does nothing.";
126 return;
127 }
128
129 DCHECK(!install_dir.empty());
130 DVLOG(1) << "Component ready, version " << version.GetString() << " in "
131 << install_dir.value();
132
133 base::ThreadPool::PostTaskAndReplyWithResult(
134 FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
135 base::BindOnce(
136 &ReadComponentFiles, install_dir.Append(kSmartDimMetaJsonFileName),
137 install_dir.Append(kSmartDimFeaturePreprocessorConfigFileName),
138 install_dir.Append(kSmartDimModelFileName)),
139 base::BindOnce(&UpdateSmartDimMlAgent));
140 }
141
142 // Called during startup and installation before ComponentReady().
VerifyInstallation(const base::DictionaryValue & manifest,const base::FilePath & install_dir) const143 bool SmartDimComponentInstallerPolicy::VerifyInstallation(
144 const base::DictionaryValue& manifest,
145 const base::FilePath& install_dir) const {
146 // Get component version from manifest and compare to the expected_version_.
147 // Note: versions should not be treated as simple strings, for example,
148 // base::Version("2020.02.06") == base::Version("2020.2.6").
149 const auto* version_value = manifest.FindKey("version");
150 DCHECK(version_value);
151 const base::Version component_version(version_value->GetString());
152 const base::Version expected_version(expected_version_);
153 if (component_version != expected_version) {
154 DVLOG(1) << "Version " << component_version
155 << " doesn't match expected_version " << expected_version;
156 return false;
157 }
158 // No need to actually validate the pb and tflite files here, since we'll do
159 // the checking in UpdateSmartDimMlAgent.
160 return base::PathExists(
161 install_dir.Append(kSmartDimFeaturePreprocessorConfigFileName)) &&
162 base::PathExists(install_dir.Append(kSmartDimModelFileName)) &&
163 base::PathExists(install_dir.Append(kSmartDimMetaJsonFileName));
164 }
165
GetRelativeInstallDir() const166 base::FilePath SmartDimComponentInstallerPolicy::GetRelativeInstallDir() const {
167 return base::FilePath(FILE_PATH_LITERAL("SmartDim"));
168 }
169
GetHash(std::vector<uint8_t> * hash) const170 void SmartDimComponentInstallerPolicy::GetHash(
171 std::vector<uint8_t>* hash) const {
172 DCHECK(hash);
173 hash->assign(kSmartDimPublicKeySHA256,
174 kSmartDimPublicKeySHA256 + base::size(kSmartDimPublicKeySHA256));
175 }
176
GetName() const177 std::string SmartDimComponentInstallerPolicy::GetName() const {
178 return kMLSmartDimManifestName;
179 }
180
181 update_client::InstallerAttributes
GetInstallerAttributes() const182 SmartDimComponentInstallerPolicy::GetInstallerAttributes() const {
183 update_client::InstallerAttributes attrs;
184 // Append a '$' for exact matching.
185 attrs["targetversionprefix"] = expected_version_ + "$";
186 return attrs;
187 }
188
GetMimeTypes() const189 std::vector<std::string> SmartDimComponentInstallerPolicy::GetMimeTypes()
190 const {
191 return std::vector<std::string>();
192 }
193
RegisterSmartDimComponent(ComponentUpdateService * cus)194 void RegisterSmartDimComponent(ComponentUpdateService* cus) {
195 if (!base::FeatureList::IsEnabled(chromeos::features::kSmartDimNewMlAgent))
196 return;
197
198 DVLOG(1) << "Registering smart dim component.";
199 const std::string expected_version = kVersion.Get();
200
201 if (expected_version.empty()) {
202 LogComponentVersionType(ComponentVersionType::kEmpty);
203 DLOG(ERROR) << "expected_version is empty.";
204 return;
205 }
206
207 if (expected_version == kDefaultVersion)
208 LogComponentVersionType(ComponentVersionType::kDefault);
209 else
210 LogComponentVersionType(ComponentVersionType::kExperimental);
211
212 auto installer = base::MakeRefCounted<ComponentInstaller>(
213 std::make_unique<SmartDimComponentInstallerPolicy>(expected_version));
214 installer->Register(cus, base::OnceClosure());
215 }
216
217 } // namespace component_updater
218