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