blob: 9615abf5bc363522e30870a08d4af321d09fe4a6 [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/kiosk_next_home/app_controller_service.h"
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/optional.h"
#include "base/strings/string_util.h"
#include "chrome/browser/apps/app_service/app_icon_source.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/chromeos/kiosk_next_home/app_controller_service_factory.h"
#include "chrome/browser/chromeos/kiosk_next_home/intent_config_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/services/app_service/public/cpp/app_service_proxy.h"
#include "chrome/services/app_service/public/mojom/types.mojom.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/common/app.mojom.h"
#include "components/arc/session/arc_bridge_service.h"
#include "content/public/browser/url_data_source.h"
#include "ui/display/types/display_constants.h"
#include "ui/events/event_constants.h"
#include "url/gurl.h"
namespace chromeos {
namespace kiosk_next_home {
namespace {
// Returns AppInstance from ArcBridgeService if available, or nullptr.
arc::mojom::AppInstance* GetArcAppInstanceForLaunchIntent() {
return arc::ArcServiceManager::Get()
? ARC_GET_INSTANCE_FOR_METHOD(
arc::ArcServiceManager::Get()->arc_bridge_service()->app(),
LaunchIntent)
: nullptr;
}
} // namespace
// static
AppControllerService* AppControllerService::Get(
content::BrowserContext* context) {
return AppControllerServiceFactory::GetForBrowserContext(context);
}
AppControllerService::AppControllerService(Profile* profile)
: profile_(profile),
app_service_proxy_(apps::AppServiceProxyFactory::GetForProfile(profile)),
intent_config_helper_(IntentConfigHelper::GetInstance()) {
DCHECK(profile);
app_service_proxy_->AppRegistryCache().AddObserver(this);
// Add the chrome://app-icon URL data source.
// TODO(ltenorio): Move this to a more suitable location when we change
// the Kiosk Next Home to WebUI.
content::URLDataSource::Add(profile,
std::make_unique<apps::AppIconSource>(profile));
}
AppControllerService::~AppControllerService() {
app_service_proxy_->AppRegistryCache().RemoveObserver(this);
}
void AppControllerService::BindRequest(mojom::AppControllerRequest request) {
bindings_.AddBinding(this, std::move(request));
}
void AppControllerService::GetApps(
mojom::AppController::GetAppsCallback callback) {
std::vector<chromeos::kiosk_next_home::mojom::AppPtr> app_list;
// Using AppUpdate objects here since that's how the app list is intended to
// be consumed. Refer to AppRegistryCache::ForEachApp for more information.
app_service_proxy_->AppRegistryCache().ForEachApp(
[this, &app_list](const apps::AppUpdate& update) {
// Only include apps that are both relevant and installed.
if (AppIsRelevantForKioskNextHome(update) &&
update.Readiness() != apps::mojom::Readiness::kUninstalledByUser) {
app_list.push_back(CreateAppPtr(update));
}
});
std::move(callback).Run(std::move(app_list));
}
void AppControllerService::SetClient(mojom::AppControllerClientPtr client) {
client_ = std::move(client);
}
void AppControllerService::LaunchApp(const std::string& app_id) {
app_service_proxy_->Launch(app_id, ui::EventFlags::EF_NONE,
apps::mojom::LaunchSource::kFromKioskNextHome,
display::kDefaultDisplayId);
}
void AppControllerService::UninstallApp(const std::string& app_id) {
app_service_proxy_->Uninstall(app_id);
}
void AppControllerService::GetArcAndroidId(
mojom::AppController::GetArcAndroidIdCallback callback) {
arc::GetAndroidId(base::BindOnce(
[](mojom::AppController::GetArcAndroidIdCallback callback, bool success,
int64_t raw_android_id) {
// The bridge expects the Android id as a hex string.
std::stringstream android_id_stream;
android_id_stream << std::hex << raw_android_id;
std::move(callback).Run(success, android_id_stream.str());
},
std::move(callback)));
}
void AppControllerService::LaunchIntent(const std::string& intent,
LaunchIntentCallback callback) {
GURL intent_uri(intent);
if (!intent_config_helper_->IsIntentAllowed(intent_uri)) {
std::move(callback).Run(false, "Intent not allowed.");
return;
}
arc::mojom::AppInstance* app_instance = GetArcAppInstanceForLaunchIntent();
if (!app_instance) {
std::move(callback).Run(false, "ARC bridge not available.");
return;
}
app_instance->LaunchIntent(intent_uri.spec(), display::kDefaultDisplayId);
std::move(callback).Run(true, base::nullopt);
}
void AppControllerService::OnAppUpdate(const apps::AppUpdate& update) {
// Skip this event if there were no changes to the fields that we are
// interested in.
if (!update.StateIsNull() && !update.NameChanged() &&
!update.ReadinessChanged() && !update.ShowInLauncherChanged()) {
return;
}
// Skip this app if it's not relevant.
if (!AppIsRelevantForKioskNextHome(update))
return;
if (client_)
client_->OnAppChanged(CreateAppPtr(update));
}
void AppControllerService::SetIntentConfigHelperForTesting(
std::unique_ptr<IntentConfigHelper> helper) {
intent_config_helper_ = std::move(helper);
}
mojom::AppPtr AppControllerService::CreateAppPtr(
const apps::AppUpdate& update) {
auto app = chromeos::kiosk_next_home::mojom::App::New();
app->app_id = update.AppId();
app->type = update.AppType();
app->display_name = update.Name();
app->readiness = update.Readiness();
if (app->type == apps::mojom::AppType::kArc)
app->android_package_name = MaybeGetAndroidPackageName(app->app_id);
return app;
}
bool AppControllerService::AppIsRelevantForKioskNextHome(
const apps::AppUpdate& update) {
// The Kiosk Next Home app should never be returned since it's considered an
// implementation detail.
if (update.AppId() == extension_misc::kKioskNextHomeAppId)
return false;
// We only consider relevant apps that can be shown in the launcher.
// This skips hidden apps like Galery, Web store, Welcome app, etc.
return update.ShowInLauncher() == apps::mojom::OptionalBool::kTrue;
}
const std::string& AppControllerService::MaybeGetAndroidPackageName(
const std::string& app_id) {
// Try to find a cached package name for this app.
const auto& package_name_it = android_package_map_.find(app_id);
if (package_name_it != android_package_map_.end()) {
return package_name_it->second;
}
// If we don't find it, try to get the package name from ARC prefs.
ArcAppListPrefs* arc_prefs_ = ArcAppListPrefs::Get(profile_);
if (!arc_prefs_) {
return base::EmptyString();
}
std::unique_ptr<ArcAppListPrefs::AppInfo> arc_info =
arc_prefs_->GetApp(app_id);
if (!arc_info) {
return base::EmptyString();
}
// Now that we have a valid package name, update our caches.
android_package_map_[app_id] = arc_info->package_name;
return android_package_map_[app_id];
}
} // namespace kiosk_next_home
} // namespace chromeos