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 #include "chrome/browser/chromeos/policy/app_install_event_logger.h"
6
7 #include <stdint.h>
8
9 #include <algorithm>
10 #include <iterator>
11
12 #include "base/bind.h"
13 #include "base/files/file_path.h"
14 #include "base/location.h"
15 #include "base/system/sys_info.h"
16 #include "base/task/post_task.h"
17 #include "base/task/task_traits.h"
18 #include "base/task/thread_pool.h"
19 #include "base/time/time.h"
20 #include "base/values.h"
21 #include "chrome/browser/chromeos/arc/arc_util.h"
22 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
23 #include "chrome/browser/policy/profile_policy_connector.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chromeos/disks/disk.h"
26 #include "chromeos/disks/disk_mount_manager.h"
27 #include "components/arc/arc_prefs.h"
28 #include "components/policy/core/common/policy_map.h"
29 #include "components/policy/core/common/policy_namespace.h"
30 #include "components/policy/policy_constants.h"
31 #include "components/policy/proto/device_management_backend.pb.h"
32 #include "components/pref_registry/pref_registry_syncable.h"
33 #include "components/prefs/pref_service.h"
34
35 namespace em = enterprise_management;
36
37 namespace policy {
38
39 namespace {
40
41 constexpr int kNonComplianceReasonAppNotInstalled = 5;
42
GetRequestedPackagesFromPolicy(const policy::PolicyMap & policy)43 std::set<std::string> GetRequestedPackagesFromPolicy(
44 const policy::PolicyMap& policy) {
45 const base::Value* const arc_enabled = policy.GetValue(key::kArcEnabled);
46 if (!arc_enabled || !arc_enabled->is_bool() || !arc_enabled->GetBool()) {
47 return {};
48 }
49
50 const base::Value* const arc_policy = policy.GetValue(key::kArcPolicy);
51 if (!arc_policy || !arc_policy->is_string()) {
52 return {};
53 }
54
55 return arc::policy_util::GetRequestedPackagesFromArcPolicy(
56 arc_policy->GetString());
57 }
58
59 // Return all elements that are members of |first| but not |second|.
GetDifference(const std::set<std::string> & first,const std::set<std::string> & second)60 std::set<std::string> GetDifference(const std::set<std::string>& first,
61 const std::set<std::string>& second) {
62 std::set<std::string> difference;
63 std::set_difference(first.begin(), first.end(), second.begin(), second.end(),
64 std::inserter(difference, difference.end()));
65 return difference;
66 }
67
AddDiskSpaceInfoToEvent(std::unique_ptr<em::AppInstallReportLogEvent> event)68 std::unique_ptr<em::AppInstallReportLogEvent> AddDiskSpaceInfoToEvent(
69 std::unique_ptr<em::AppInstallReportLogEvent> event) {
70 for (const auto& disk :
71 chromeos::disks::DiskMountManager::GetInstance()->disks()) {
72 if (!disk.second->IsStatefulPartition()) {
73 continue;
74 }
75 const base::FilePath stateful_path(disk.second->mount_path());
76 const int64_t stateful_total =
77 base::SysInfo::AmountOfTotalDiskSpace(stateful_path);
78 if (stateful_total >= 0) {
79 event->set_stateful_total(stateful_total);
80 }
81 const int64_t stateful_free =
82 base::SysInfo::AmountOfFreeDiskSpace(stateful_path);
83 if (stateful_free >= 0) {
84 event->set_stateful_free(stateful_free);
85 }
86 break;
87 }
88 return event;
89 }
90
EnsureTimestampSet(em::AppInstallReportLogEvent * event)91 void EnsureTimestampSet(em::AppInstallReportLogEvent* event) {
92 if (!event->has_timestamp()) {
93 event->set_timestamp(
94 (base::Time::Now() - base::Time::UnixEpoch()).InMicroseconds());
95 }
96 }
97
CreateEvent(em::AppInstallReportLogEvent::EventType type)98 std::unique_ptr<em::AppInstallReportLogEvent> CreateEvent(
99 em::AppInstallReportLogEvent::EventType type) {
100 std::unique_ptr<em::AppInstallReportLogEvent> event =
101 std::make_unique<em::AppInstallReportLogEvent>();
102 EnsureTimestampSet(event.get());
103 event->set_event_type(type);
104 return event;
105 }
106
107 } // namespace
108
AppInstallEventLogger(Delegate * delegate,Profile * profile)109 AppInstallEventLogger::AppInstallEventLogger(Delegate* delegate,
110 Profile* profile)
111 : delegate_(delegate), profile_(profile) {
112 if (!arc::IsArcAllowedForProfile(profile_)) {
113 AddForSetOfPackages(
114 GetPackagesFromPref(arc::prefs::kArcPushInstallAppsPending),
115 CreateEvent(em::AppInstallReportLogEvent::CANCELED));
116 Clear(profile_);
117 return;
118 }
119
120 policy::PolicyService* const policy_service =
121 profile_->GetProfilePolicyConnector()->policy_service();
122 EvaluatePolicy(policy_service->GetPolicies(policy::PolicyNamespace(
123 policy::POLICY_DOMAIN_CHROME, std::string())),
124 true /* initial */);
125
126 observing_ = true;
127 arc::ArcPolicyBridge* bridge =
128 arc::ArcPolicyBridge::GetForBrowserContext(profile_);
129 bridge->AddObserver(this);
130 policy_service->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
131 }
132
~AppInstallEventLogger()133 AppInstallEventLogger::~AppInstallEventLogger() {
134 if (log_collector_) {
135 log_collector_->AddLogoutEvent();
136 }
137 if (observing_) {
138 arc::ArcPolicyBridge::GetForBrowserContext(profile_)->RemoveObserver(this);
139 profile_->GetProfilePolicyConnector()->policy_service()->RemoveObserver(
140 policy::POLICY_DOMAIN_CHROME, this);
141 }
142 }
143
144 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)145 void AppInstallEventLogger::RegisterProfilePrefs(
146 user_prefs::PrefRegistrySyncable* registry) {
147 registry->RegisterListPref(arc::prefs::kArcPushInstallAppsRequested);
148 registry->RegisterListPref(arc::prefs::kArcPushInstallAppsPending);
149 }
150
151 // static
Clear(Profile * profile)152 void AppInstallEventLogger::Clear(Profile* profile) {
153 profile->GetPrefs()->ClearPref(arc::prefs::kArcPushInstallAppsRequested);
154 profile->GetPrefs()->ClearPref(arc::prefs::kArcPushInstallAppsPending);
155 }
156
AddForAllPackages(std::unique_ptr<em::AppInstallReportLogEvent> event)157 void AppInstallEventLogger::AddForAllPackages(
158 std::unique_ptr<em::AppInstallReportLogEvent> event) {
159 EnsureTimestampSet(event.get());
160 AddForSetOfPackages(
161 GetPackagesFromPref(arc::prefs::kArcPushInstallAppsPending),
162 std::move(event));
163 }
164
Add(const std::string & package,bool gather_disk_space_info,std::unique_ptr<em::AppInstallReportLogEvent> event)165 void AppInstallEventLogger::Add(
166 const std::string& package,
167 bool gather_disk_space_info,
168 std::unique_ptr<em::AppInstallReportLogEvent> event) {
169 EnsureTimestampSet(event.get());
170 if (gather_disk_space_info) {
171 AddForSetOfPackagesWithDiskSpaceInfo({package}, std::move(event));
172 } else {
173 AddForSetOfPackages({package}, std::move(event));
174 }
175 }
176
OnPolicyUpdated(const policy::PolicyNamespace & ns,const policy::PolicyMap & previous,const policy::PolicyMap & current)177 void AppInstallEventLogger::OnPolicyUpdated(const policy::PolicyNamespace& ns,
178 const policy::PolicyMap& previous,
179 const policy::PolicyMap& current) {
180 EvaluatePolicy(current, false /* initial */);
181 }
182
OnPolicySent(const std::string & policy)183 void AppInstallEventLogger::OnPolicySent(const std::string& policy) {
184 requested_in_arc_ =
185 arc::policy_util::GetRequestedPackagesFromArcPolicy(policy);
186 }
187
OnComplianceReportReceived(const base::Value * compliance_report)188 void AppInstallEventLogger::OnComplianceReportReceived(
189 const base::Value* compliance_report) {
190 const base::Value* const details = compliance_report->FindKeyOfType(
191 "nonComplianceDetails", base::Value::Type::LIST);
192 if (!details) {
193 return;
194 }
195
196 const std::set<std::string> previous_pending =
197 GetPackagesFromPref(arc::prefs::kArcPushInstallAppsPending);
198
199 std::set<std::string> pending_in_arc;
200 for (const auto& detail : details->GetList()) {
201 const base::Value* const reason =
202 detail.FindKeyOfType("nonComplianceReason", base::Value::Type::INTEGER);
203 if (!reason || reason->GetInt() != kNonComplianceReasonAppNotInstalled) {
204 continue;
205 }
206 const base::Value* const app_name =
207 detail.FindKeyOfType("packageName", base::Value::Type::STRING);
208 if (!app_name || app_name->GetString().empty()) {
209 continue;
210 }
211 pending_in_arc.insert(app_name->GetString());
212 }
213 const std::set<std::string> current_pending = GetDifference(
214 previous_pending, GetDifference(requested_in_arc_, pending_in_arc));
215 const std::set<std::string> removed =
216 GetDifference(previous_pending, current_pending);
217 AddForSetOfPackagesWithDiskSpaceInfo(
218 removed, CreateEvent(em::AppInstallReportLogEvent::SUCCESS));
219
220 if (removed.empty()) {
221 return;
222 }
223
224 SetPref(arc::prefs::kArcPushInstallAppsPending, current_pending);
225
226 if (!current_pending.empty()) {
227 UpdateCollector(current_pending);
228 } else {
229 StopCollector();
230 }
231 }
232
GetPackagesFromPref(const std::string & pref_name) const233 std::set<std::string> AppInstallEventLogger::GetPackagesFromPref(
234 const std::string& pref_name) const {
235 std::set<std::string> packages;
236 for (const auto& package :
237 profile_->GetPrefs()->GetList(pref_name)->GetList()) {
238 if (!package.is_string()) {
239 continue;
240 }
241 packages.insert(package.GetString());
242 }
243 return packages;
244 }
245
SetPref(const std::string & pref_name,const std::set<std::string> & packages)246 void AppInstallEventLogger::SetPref(const std::string& pref_name,
247 const std::set<std::string>& packages) {
248 base::Value value(base::Value::Type::LIST);
249 for (const std::string& package : packages) {
250 value.Append(package);
251 }
252 profile_->GetPrefs()->Set(pref_name, value);
253 }
254
UpdateCollector(const std::set<std::string> & pending)255 void AppInstallEventLogger::UpdateCollector(
256 const std::set<std::string>& pending) {
257 if (!log_collector_) {
258 log_collector_ =
259 std::make_unique<AppInstallEventLogCollector>(this, profile_, pending);
260 } else {
261 log_collector_->OnPendingPackagesChanged(pending);
262 }
263 }
264
StopCollector()265 void AppInstallEventLogger::StopCollector() {
266 log_collector_.reset();
267 }
268
EvaluatePolicy(const policy::PolicyMap & policy,bool initial)269 void AppInstallEventLogger::EvaluatePolicy(const policy::PolicyMap& policy,
270 bool initial) {
271 const std::set<std::string> previous_requested =
272 GetPackagesFromPref(arc::prefs::kArcPushInstallAppsRequested);
273 const std::set<std::string> previous_pending =
274 GetPackagesFromPref(arc::prefs::kArcPushInstallAppsPending);
275
276 const std::set<std::string> current_requested =
277 GetRequestedPackagesFromPolicy(policy);
278
279 const std::set<std::string> added =
280 GetDifference(current_requested, previous_requested);
281 const std::set<std::string> removed =
282 GetDifference(previous_pending, current_requested);
283 AddForSetOfPackagesWithDiskSpaceInfo(
284 added, CreateEvent(em::AppInstallReportLogEvent::SERVER_REQUEST));
285 AddForSetOfPackages(removed,
286 CreateEvent(em::AppInstallReportLogEvent::CANCELED));
287
288 const std::set<std::string> current_pending = GetDifference(
289 current_requested, GetDifference(previous_requested, previous_pending));
290 SetPref(arc::prefs::kArcPushInstallAppsRequested, current_requested);
291 SetPref(arc::prefs::kArcPushInstallAppsPending, current_pending);
292
293 if (!current_pending.empty()) {
294 UpdateCollector(current_pending);
295 if (initial) {
296 log_collector_->AddLoginEvent();
297 }
298 } else {
299 StopCollector();
300 }
301 }
302
AddForSetOfPackagesWithDiskSpaceInfo(const std::set<std::string> & packages,std::unique_ptr<em::AppInstallReportLogEvent> event)303 void AppInstallEventLogger::AddForSetOfPackagesWithDiskSpaceInfo(
304 const std::set<std::string>& packages,
305 std::unique_ptr<em::AppInstallReportLogEvent> event) {
306 base::ThreadPool::PostTaskAndReplyWithResult(
307 FROM_HERE, {base::MayBlock()},
308 base::BindOnce(&AddDiskSpaceInfoToEvent, std::move(event)),
309 base::BindOnce(&AppInstallEventLogger::AddForSetOfPackages,
310 weak_factory_.GetWeakPtr(), packages));
311 }
312
AddForSetOfPackages(const std::set<std::string> & packages,std::unique_ptr<em::AppInstallReportLogEvent> event)313 void AppInstallEventLogger::AddForSetOfPackages(
314 const std::set<std::string>& packages,
315 std::unique_ptr<em::AppInstallReportLogEvent> event) {
316 delegate_->GetAndroidId(base::BindOnce(&AppInstallEventLogger::OnGetAndroidId,
317 weak_factory_.GetWeakPtr(), packages,
318 std::move(event)));
319 }
320
OnGetAndroidId(const std::set<std::string> & packages,std::unique_ptr<em::AppInstallReportLogEvent> event,bool ok,int64_t android_id)321 void AppInstallEventLogger::OnGetAndroidId(
322 const std::set<std::string>& packages,
323 std::unique_ptr<em::AppInstallReportLogEvent> event,
324 bool ok,
325 int64_t android_id) {
326 if (ok) {
327 event->set_android_id(android_id);
328 }
329 delegate_->Add(packages, *event);
330 }
331
332 } // namespace policy
333