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_log_collector.h"
6
7 #include <vector>
8
9 #include "base/command_line.h"
10 #include "base/macros.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/run_loop.h"
13 #include "base/time/time.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/prefs/browser_prefs.h"
16 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/test/base/testing_browser_process.h"
19 #include "chrome/test/base/testing_profile.h"
20 #include "chromeos/constants/chromeos_switches.h"
21 #include "chromeos/dbus/dbus_thread_manager.h"
22 #include "chromeos/dbus/power/fake_power_manager_client.h"
23 #include "chromeos/dbus/shill/shill_service_client.h"
24 #include "chromeos/network/network_handler.h"
25 #include "components/arc/mojom/app.mojom.h"
26 #include "components/policy/proto/device_management_backend.pb.h"
27 #include "components/prefs/pref_service.h"
28 #include "components/prefs/testing_pref_service.h"
29 #include "content/public/test/browser_task_environment.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "third_party/cros_system_api/dbus/service_constants.h"
32
33 namespace em = enterprise_management;
34
35 namespace policy {
36
37 namespace {
38
39 constexpr char kEthernetServicePath[] = "/service/eth1";
40 constexpr char kWifiServicePath[] = "/service/wifi1";
41
42 constexpr char kPackageName[] = "com.example.app";
43 constexpr char kPackageName2[] = "com.example.app2";
44
45 class FakeAppInstallEventLogCollectorDelegate
46 : public AppInstallEventLogCollector::Delegate {
47 public:
48 FakeAppInstallEventLogCollectorDelegate() = default;
49 ~FakeAppInstallEventLogCollectorDelegate() override = default;
50
51 struct Request {
Requestpolicy::__anon9442a5250111::FakeAppInstallEventLogCollectorDelegate::Request52 Request(bool for_all,
53 bool add_disk_space_info,
54 const std::string& package_name,
55 const em::AppInstallReportLogEvent& event)
56 : for_all(for_all),
57 add_disk_space_info(add_disk_space_info),
58 package_name(package_name),
59 event(event) {}
60 const bool for_all;
61 const bool add_disk_space_info;
62 const std::string package_name;
63 const em::AppInstallReportLogEvent event;
64 };
65
66 // AppInstallEventLogCollector::Delegate:
AddForAllPackages(std::unique_ptr<em::AppInstallReportLogEvent> event)67 void AddForAllPackages(
68 std::unique_ptr<em::AppInstallReportLogEvent> event) override {
69 ++add_for_all_count_;
70 requests_.emplace_back(true /* for_all */, false /* add_disk_space_info */,
71 std::string() /* package_name */, *event);
72 }
73
Add(const std::string & package_name,bool add_disk_space_info,std::unique_ptr<em::AppInstallReportLogEvent> event)74 void Add(const std::string& package_name,
75 bool add_disk_space_info,
76 std::unique_ptr<em::AppInstallReportLogEvent> event) override {
77 ++add_count_;
78 requests_.emplace_back(false /* for_all */, add_disk_space_info,
79 package_name, *event);
80 }
81
add_for_all_count() const82 int add_for_all_count() const { return add_for_all_count_; }
83
add_count() const84 int add_count() const { return add_count_; }
85
last_event() const86 const em::AppInstallReportLogEvent& last_event() const {
87 return last_request().event;
88 }
last_request() const89 const Request& last_request() const { return requests_.back(); }
requests() const90 const std::vector<Request>& requests() const { return requests_; }
91
92 private:
93 int add_for_all_count_ = 0;
94 int add_count_ = 0;
95 std::vector<Request> requests_;
96
97 DISALLOW_COPY_AND_ASSIGN(FakeAppInstallEventLogCollectorDelegate);
98 };
99
TimeToTimestamp(base::Time time)100 int64_t TimeToTimestamp(base::Time time) {
101 return (time - base::Time::UnixEpoch()).InMicroseconds();
102 }
103
104 } // namespace
105
106 class AppInstallEventLogCollectorTest : public testing::Test {
107 protected:
108 AppInstallEventLogCollectorTest() = default;
109 ~AppInstallEventLogCollectorTest() override = default;
110
SetUp()111 void SetUp() override {
112 RegisterLocalState(pref_service_.registry());
113 TestingBrowserProcess::GetGlobal()->SetLocalState(&pref_service_);
114
115 chromeos::DBusThreadManager::Initialize();
116 chromeos::PowerManagerClient::InitializeFake();
117 chromeos::NetworkHandler::Initialize();
118 profile_ = std::make_unique<TestingProfile>();
119
120 service_test_ = chromeos::DBusThreadManager::Get()
121 ->GetShillServiceClient()
122 ->GetTestInterface();
123 service_test_->AddService(kEthernetServicePath, "eth1_guid", "eth1",
124 shill::kTypeEthernet, shill::kStateOffline,
125 true /* visible */);
126 service_test_->AddService(kWifiServicePath, "wifi1_guid", "wifi1",
127 shill::kTypeEthernet, shill::kStateOffline,
128 true /* visible */);
129 base::RunLoop().RunUntilIdle();
130
131 arc_app_test_.SetUp(profile_.get());
132 }
133
TearDown()134 void TearDown() override {
135 arc_app_test_.TearDown();
136
137 profile_.reset();
138 chromeos::NetworkHandler::Shutdown();
139 chromeos::PowerManagerClient::Shutdown();
140 chromeos::DBusThreadManager::Shutdown();
141 TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
142 }
143
SetNetworkState(network::NetworkConnectionTracker::NetworkConnectionObserver * observer,const std::string & service_path,const std::string & state)144 void SetNetworkState(
145 network::NetworkConnectionTracker::NetworkConnectionObserver* observer,
146 const std::string& service_path,
147 const std::string& state) {
148 service_test_->SetServiceProperty(service_path, shill::kStateProperty,
149 base::Value(state));
150 base::RunLoop().RunUntilIdle();
151
152 network::mojom::ConnectionType connection_type =
153 network::mojom::ConnectionType::CONNECTION_NONE;
154 const std::string* network_state =
155 service_test_->GetServiceProperties(kWifiServicePath)
156 ->FindStringKey(shill::kStateProperty);
157 if (network_state && *network_state == shill::kStateOnline) {
158 connection_type = network::mojom::ConnectionType::CONNECTION_WIFI;
159 }
160 network_state = service_test_->GetServiceProperties(kEthernetServicePath)
161 ->FindStringKey(shill::kStateProperty);
162 if (network_state && *network_state == shill::kStateOnline) {
163 connection_type = network::mojom::ConnectionType::CONNECTION_ETHERNET;
164 }
165 if (observer)
166 observer->OnConnectionChanged(connection_type);
167 base::RunLoop().RunUntilIdle();
168 }
169
profile()170 TestingProfile* profile() { return profile_.get(); }
delegate()171 FakeAppInstallEventLogCollectorDelegate* delegate() { return &delegate_; }
app_prefs()172 ArcAppListPrefs* app_prefs() { return arc_app_test_.arc_app_list_prefs(); }
173
174 const std::set<std::string> packages_ = {kPackageName};
175
176 chromeos::ShillServiceClient::TestInterface* service_test_ = nullptr;
177
178 private:
179 content::BrowserTaskEnvironment task_environment_;
180 std::unique_ptr<TestingProfile> profile_;
181 FakeAppInstallEventLogCollectorDelegate delegate_;
182 TestingPrefServiceSimple pref_service_;
183 ArcAppTest arc_app_test_;
184
185 DISALLOW_COPY_AND_ASSIGN(AppInstallEventLogCollectorTest);
186 };
187
188 // Test the case when collector is created and destroyed inside the one user
189 // session. In this case no event is generated. This happens for example when
190 // all apps are installed in context of the same user session.
TEST_F(AppInstallEventLogCollectorTest,NoEventsByDefault)191 TEST_F(AppInstallEventLogCollectorTest, NoEventsByDefault) {
192 std::unique_ptr<AppInstallEventLogCollector> collector =
193 std::make_unique<AppInstallEventLogCollector>(delegate(), profile(),
194 packages_);
195 collector.reset();
196
197 EXPECT_EQ(0, delegate()->add_count());
198 EXPECT_EQ(0, delegate()->add_for_all_count());
199 }
200
TEST_F(AppInstallEventLogCollectorTest,LoginLogout)201 TEST_F(AppInstallEventLogCollectorTest, LoginLogout) {
202 std::unique_ptr<AppInstallEventLogCollector> collector =
203 std::make_unique<AppInstallEventLogCollector>(delegate(), profile(),
204 packages_);
205
206 EXPECT_EQ(0, delegate()->add_for_all_count());
207
208 collector->AddLoginEvent();
209 EXPECT_EQ(1, delegate()->add_for_all_count());
210 EXPECT_EQ(em::AppInstallReportLogEvent::SESSION_STATE_CHANGE,
211 delegate()->last_event().event_type());
212 EXPECT_EQ(em::AppInstallReportLogEvent::LOGIN,
213 delegate()->last_event().session_state_change_type());
214 EXPECT_TRUE(delegate()->last_event().has_online());
215 EXPECT_FALSE(delegate()->last_event().online());
216
217 collector->AddLogoutEvent();
218 EXPECT_EQ(2, delegate()->add_for_all_count());
219 EXPECT_EQ(em::AppInstallReportLogEvent::SESSION_STATE_CHANGE,
220 delegate()->last_event().event_type());
221 EXPECT_EQ(em::AppInstallReportLogEvent::LOGOUT,
222 delegate()->last_event().session_state_change_type());
223 EXPECT_FALSE(delegate()->last_event().has_online());
224
225 collector.reset();
226
227 EXPECT_EQ(2, delegate()->add_for_all_count());
228 EXPECT_EQ(0, delegate()->add_count());
229 }
230
TEST_F(AppInstallEventLogCollectorTest,LoginTypes)231 TEST_F(AppInstallEventLogCollectorTest, LoginTypes) {
232 {
233 AppInstallEventLogCollector collector(delegate(), profile(), packages_);
234 collector.AddLoginEvent();
235 EXPECT_EQ(1, delegate()->add_for_all_count());
236 EXPECT_EQ(em::AppInstallReportLogEvent::SESSION_STATE_CHANGE,
237 delegate()->last_event().event_type());
238 EXPECT_EQ(em::AppInstallReportLogEvent::LOGIN,
239 delegate()->last_event().session_state_change_type());
240 EXPECT_TRUE(delegate()->last_event().has_online());
241 EXPECT_FALSE(delegate()->last_event().online());
242 }
243
244 {
245 // Check login after restart. No log is expected.
246 AppInstallEventLogCollector collector(delegate(), profile(), packages_);
247 base::CommandLine::ForCurrentProcess()->AppendSwitch(
248 chromeos::switches::kLoginUser);
249 collector.AddLoginEvent();
250 EXPECT_EQ(1, delegate()->add_for_all_count());
251 }
252
253 {
254 // Check logout on restart. No log is expected.
255 AppInstallEventLogCollector collector(delegate(), profile(), packages_);
256 g_browser_process->local_state()->SetBoolean(prefs::kWasRestarted, true);
257 collector.AddLogoutEvent();
258 EXPECT_EQ(1, delegate()->add_for_all_count());
259 }
260
261 EXPECT_EQ(0, delegate()->add_count());
262 }
263
TEST_F(AppInstallEventLogCollectorTest,SuspendResume)264 TEST_F(AppInstallEventLogCollectorTest, SuspendResume) {
265 std::unique_ptr<AppInstallEventLogCollector> collector =
266 std::make_unique<AppInstallEventLogCollector>(delegate(), profile(),
267 packages_);
268
269 chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(
270 power_manager::SuspendImminent_Reason_OTHER);
271 EXPECT_EQ(1, delegate()->add_for_all_count());
272 EXPECT_EQ(em::AppInstallReportLogEvent::SESSION_STATE_CHANGE,
273 delegate()->last_event().event_type());
274 EXPECT_EQ(em::AppInstallReportLogEvent::SUSPEND,
275 delegate()->last_event().session_state_change_type());
276
277 chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
278 EXPECT_EQ(2, delegate()->add_for_all_count());
279 EXPECT_EQ(em::AppInstallReportLogEvent::SESSION_STATE_CHANGE,
280 delegate()->last_event().event_type());
281 EXPECT_EQ(em::AppInstallReportLogEvent::RESUME,
282 delegate()->last_event().session_state_change_type());
283
284 collector.reset();
285
286 EXPECT_EQ(0, delegate()->add_count());
287 }
288
289 // Connect to Ethernet. Start log collector. Verify that a login event with
290 // network state online is recorded. Then, connect to WiFi and disconnect from
291 // Ethernet, in this order. Verify that no event is recorded. Then, disconnect
292 // from WiFi. Verify that a connectivity change event is recorded. Then, connect
293 // to WiFi with a pending captive portal. Verify that no event is recorded.
294 // Then, pass the captive portal. Verify that a connectivity change is recorded.
TEST_F(AppInstallEventLogCollectorTest,ConnectivityChanges)295 TEST_F(AppInstallEventLogCollectorTest, ConnectivityChanges) {
296 SetNetworkState(nullptr, kEthernetServicePath, shill::kStateOnline);
297
298 std::unique_ptr<AppInstallEventLogCollector> collector =
299 std::make_unique<AppInstallEventLogCollector>(delegate(), profile(),
300 packages_);
301
302 EXPECT_EQ(0, delegate()->add_for_all_count());
303
304 collector->AddLoginEvent();
305 EXPECT_EQ(1, delegate()->add_for_all_count());
306 EXPECT_EQ(em::AppInstallReportLogEvent::SESSION_STATE_CHANGE,
307 delegate()->last_event().event_type());
308 EXPECT_EQ(em::AppInstallReportLogEvent::LOGIN,
309 delegate()->last_event().session_state_change_type());
310 EXPECT_TRUE(delegate()->last_event().online());
311
312 SetNetworkState(collector.get(), kWifiServicePath, shill::kStateOnline);
313 EXPECT_EQ(1, delegate()->add_for_all_count());
314
315 SetNetworkState(collector.get(), kEthernetServicePath, shill::kStateOffline);
316 EXPECT_EQ(1, delegate()->add_for_all_count());
317
318 SetNetworkState(collector.get(), kWifiServicePath, shill::kStateOffline);
319 EXPECT_EQ(2, delegate()->add_for_all_count());
320 EXPECT_EQ(em::AppInstallReportLogEvent::CONNECTIVITY_CHANGE,
321 delegate()->last_event().event_type());
322 EXPECT_FALSE(delegate()->last_event().online());
323
324 SetNetworkState(collector.get(), kWifiServicePath,
325 shill::kStateNoConnectivity);
326 EXPECT_EQ(2, delegate()->add_for_all_count());
327
328 SetNetworkState(collector.get(), kWifiServicePath, shill::kStateOnline);
329 EXPECT_EQ(3, delegate()->add_for_all_count());
330 EXPECT_EQ(em::AppInstallReportLogEvent::CONNECTIVITY_CHANGE,
331 delegate()->last_event().event_type());
332 EXPECT_TRUE(delegate()->last_event().online());
333
334 collector.reset();
335
336 EXPECT_EQ(3, delegate()->add_for_all_count());
337 EXPECT_EQ(0, delegate()->add_count());
338 }
339
340 // Validates sequence of CloudDPS events.
TEST_F(AppInstallEventLogCollectorTest,CloudDPSEvent)341 TEST_F(AppInstallEventLogCollectorTest, CloudDPSEvent) {
342 std::unique_ptr<AppInstallEventLogCollector> collector =
343 std::make_unique<AppInstallEventLogCollector>(delegate(), profile(),
344 packages_);
345
346 base::Time time = base::Time::Now();
347 collector->OnCloudDpsRequested(time, {kPackageName, kPackageName2});
348 ASSERT_EQ(2, delegate()->add_count());
349 ASSERT_EQ(0, delegate()->add_for_all_count());
350 EXPECT_EQ(TimeToTimestamp(time), delegate()->requests()[0].event.timestamp());
351 EXPECT_EQ(kPackageName, delegate()->requests()[0].package_name);
352 EXPECT_EQ(em::AppInstallReportLogEvent::CLOUDDPS_REQUEST,
353 delegate()->requests()[0].event.event_type());
354 EXPECT_FALSE(delegate()->requests()[0].event.has_clouddps_response());
355 EXPECT_EQ(TimeToTimestamp(time), delegate()->requests()[1].event.timestamp());
356 EXPECT_EQ(kPackageName2, delegate()->requests()[1].package_name);
357 EXPECT_EQ(em::AppInstallReportLogEvent::CLOUDDPS_REQUEST,
358 delegate()->requests()[1].event.event_type());
359 EXPECT_EQ(0, delegate()->requests()[1].event.clouddps_response());
360
361 // One package succeeded.
362 time += base::TimeDelta::FromSeconds(1);
363 collector->OnCloudDpsSucceeded(time, {kPackageName});
364 ASSERT_EQ(3, delegate()->add_count());
365 ASSERT_EQ(0, delegate()->add_for_all_count());
366 EXPECT_EQ(TimeToTimestamp(time),
367 delegate()->last_request().event.timestamp());
368 EXPECT_EQ(kPackageName, delegate()->last_request().package_name);
369 EXPECT_EQ(em::AppInstallReportLogEvent::CLOUDDPS_RESPONSE,
370 delegate()->last_request().event.event_type());
371 EXPECT_FALSE(delegate()->requests()[0].event.has_clouddps_response());
372
373 // One package failed.
374 time += base::TimeDelta::FromSeconds(1);
375 collector->OnCloudDpsFailed(time, kPackageName2,
376 arc::mojom::InstallErrorReason::TIMEOUT);
377 ASSERT_EQ(4, delegate()->add_count());
378 ASSERT_EQ(0, delegate()->add_for_all_count());
379 EXPECT_EQ(TimeToTimestamp(time),
380 delegate()->last_request().event.timestamp());
381 EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
382 EXPECT_EQ(em::AppInstallReportLogEvent::CLOUDDPS_RESPONSE,
383 delegate()->last_request().event.event_type());
384 EXPECT_TRUE(delegate()->last_request().event.has_clouddps_response());
385 EXPECT_EQ(static_cast<int>(arc::mojom::InstallErrorReason::TIMEOUT),
386 delegate()->last_request().event.clouddps_response());
387 }
388
TEST_F(AppInstallEventLogCollectorTest,InstallPackages)389 TEST_F(AppInstallEventLogCollectorTest, InstallPackages) {
390 arc::mojom::AppHost* const app_host = app_prefs();
391
392 std::unique_ptr<AppInstallEventLogCollector> collector =
393 std::make_unique<AppInstallEventLogCollector>(delegate(), profile(),
394 packages_);
395
396 app_host->OnInstallationStarted(kPackageName);
397 ASSERT_EQ(1, delegate()->add_count());
398 EXPECT_EQ(em::AppInstallReportLogEvent::INSTALLATION_STARTED,
399 delegate()->last_event().event_type());
400 EXPECT_EQ(kPackageName, delegate()->last_request().package_name);
401 EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
402
403 // kPackageName2 is not in the pending set.
404 app_host->OnInstallationStarted(kPackageName2);
405 EXPECT_EQ(1, delegate()->add_count());
406
407 arc::mojom::InstallationResult result;
408 result.package_name = kPackageName;
409 result.success = true;
410 app_host->OnInstallationFinished(
411 arc::mojom::InstallationResultPtr(result.Clone()));
412 EXPECT_EQ(2, delegate()->add_count());
413 EXPECT_EQ(em::AppInstallReportLogEvent::INSTALLATION_FINISHED,
414 delegate()->last_event().event_type());
415 EXPECT_EQ(kPackageName, delegate()->last_request().package_name);
416 EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
417
418 collector->OnPendingPackagesChanged({kPackageName, kPackageName2});
419
420 // Now kPackageName2 is in the pending set.
421 base::Time time = base::Time::Now();
422 collector->OnReportDirectInstall(time, {kPackageName2});
423 EXPECT_EQ(3, delegate()->add_count());
424 EXPECT_EQ(em::AppInstallReportLogEvent::DIRECT_INSTALL,
425 delegate()->last_event().event_type());
426 EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
427 EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
428
429 app_host->OnInstallationStarted(kPackageName2);
430 EXPECT_EQ(4, delegate()->add_count());
431 EXPECT_EQ(em::AppInstallReportLogEvent::INSTALLATION_STARTED,
432 delegate()->last_event().event_type());
433 EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
434 EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
435
436 result.package_name = kPackageName2;
437 result.success = false;
438 app_host->OnInstallationFinished(
439 arc::mojom::InstallationResultPtr(result.Clone()));
440 EXPECT_EQ(5, delegate()->add_count());
441 EXPECT_EQ(em::AppInstallReportLogEvent::INSTALLATION_FAILED,
442 delegate()->last_event().event_type());
443 EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
444 EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
445
446 time += base::TimeDelta::FromSeconds(1);
447 collector->OnReportForceInstallMainLoopFailed(time, {kPackageName2});
448 EXPECT_EQ(6, delegate()->add_count());
449 EXPECT_EQ(em::AppInstallReportLogEvent::CLOUDDPC_MAIN_LOOP_FAILED,
450 delegate()->last_event().event_type());
451 EXPECT_EQ(kPackageName2, delegate()->last_request().package_name);
452 EXPECT_TRUE(delegate()->last_request().add_disk_space_info);
453
454 EXPECT_EQ(0, delegate()->add_for_all_count());
455 }
456
457 } // namespace policy
458