blob: adb52f07c9a4c9c0d4320909d5feb5817057e5c5 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/arc/arc_ui_availability_reporter.h"
#include "chrome/browser/chromeos/arc/arc_optin_uma.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/session/arc_bridge_service.h"
namespace arc {
class ArcUiAvailabilityReporter::ConnectionNotifierBase {
public:
virtual ~ConnectionNotifierBase() = default;
// Returns true if connection is ready.
virtual bool IsConnected() = 0;
protected:
explicit ConnectionNotifierBase(ArcUiAvailabilityReporter* owner)
: owner_(owner) {}
ArcUiAvailabilityReporter* owner() { return owner_; }
private:
ArcUiAvailabilityReporter* const owner_;
DISALLOW_COPY_AND_ASSIGN(ConnectionNotifierBase);
};
namespace {
template <typename InstanceType, typename HostType>
class ConnectionNotifier
: public ArcUiAvailabilityReporter::ConnectionNotifierBase,
public ConnectionObserver<InstanceType> {
public:
// |owner_| owns this object and |ConnectionNotifier| is destroyed in case
// statistics is reported or ARC session is terminated on user log-out and
// this automatically destroys this object at time of destroying |owner_|.
// |holder_| is owned by ArcBridgeService this lives longer than
// |ArcUiAvailabilityReporter|.
ConnectionNotifier(ArcUiAvailabilityReporter* owner,
ConnectionHolder<InstanceType, HostType>* holder)
: ArcUiAvailabilityReporter::ConnectionNotifierBase(owner),
holder_(holder) {
DCHECK(!holder_->IsConnected());
holder_->AddObserver(this);
}
~ConnectionNotifier() override { holder_->RemoveObserver(this); }
// ArcUiAvailabilityReporter::ConnectionNotifierBase:
bool IsConnected() override { return holder_->IsConnected(); }
// ConnectionObserver<InstanceType>:
void OnConnectionReady() override { owner()->MaybeReport(); }
private:
ConnectionHolder<InstanceType, HostType>* const holder_;
DISALLOW_COPY_AND_ASSIGN(ConnectionNotifier);
};
} // namespace
ArcUiAvailabilityReporter::ArcUiAvailabilityReporter(Profile* profile,
Mode mode)
: profile_(profile), mode_(mode), start_ticks_(base::TimeTicks::Now()) {
DCHECK(profile);
// Some unit tests may not have |ArcServiceManager| set.
ArcServiceManager* const service_manager = ArcServiceManager::Get();
if (!service_manager)
return;
// |initiated_from_oobe| must be set only if |initial_start| is set.
auto* const arc_bridge = ArcServiceManager::Get()->arc_bridge_service();
// Not expected instances are connected at the time of creation, however this
// is not always preserved in tests.
if (arc_bridge->app()->IsConnected() ||
arc_bridge->intent_helper()->IsConnected()) {
LOG(ERROR) << "App and/or intent_helper instances are already connected. "
<< "This is not expected in production.";
return;
}
connection_notifiers_.emplace_back(
std::make_unique<ConnectionNotifier<mojom::AppInstance, mojom::AppHost>>(
this, arc_bridge->app()));
connection_notifiers_.emplace_back(
std::make_unique<ConnectionNotifier<mojom::IntentHelperInstance,
mojom::IntentHelperHost>>(
this, arc_bridge->intent_helper()));
}
ArcUiAvailabilityReporter::~ArcUiAvailabilityReporter() = default;
// static
std::string ArcUiAvailabilityReporter::GetHistogramNameForMode(Mode mode) {
switch (mode) {
case Mode::kOobeProvisioning:
return "OobeProvisioning";
case Mode::kInSessionProvisioning:
return "InSessionProvisioning";
case Mode::kAlreadyProvisioned:
return "AlreadyProvisioned";
}
}
void ArcUiAvailabilityReporter::MaybeReport() {
DCHECK(!connection_notifiers_.empty());
// Check that all tracked instance are connected.
for (const auto& connection_notifier : connection_notifiers_) {
if (!connection_notifier->IsConnected())
return;
}
UpdateArcUiAvailableTime(base::TimeTicks::Now() - start_ticks_,
GetHistogramNameForMode(mode_), profile_);
// No more reporting is expected.
connection_notifiers_.clear();
}
} // namespace arc