Begin implementation of Crostini App Registry
This patch begins the implementation of the Crostini App Registry, which
will be used to maintain a cache of the installed apps so that we can
populate the app launcher without requiring Crostini to be running.
For now we just support the most basic fields from .desktop files, but
this will be expanded later to support other fields and localization.
We key the stored data off an extension id generated from the desktop
file id to simplify integration with the App List, which currently
expects the id to be a valid extension id.
Bug: 821662
Change-Id: I196de114cf975c1402dce4b7550bc4eb5ce0ef1e
Reviewed-on: https://chromium-review.googlesource.com/961169
Reviewed-by: Xiyuan Xia <[email protected]>
Reviewed-by: Nicholas Verne <[email protected]>
Reviewed-by: Sam McNally <[email protected]>
Reviewed-by: Bernhard Bauer <[email protected]>
Commit-Queue: Timothy Loh <[email protected]>
Cr-Commit-Position: refs/heads/master@{#544929}
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 9ea03610..b90c06b 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -490,6 +490,10 @@
"chrome_browser_main_chromeos.h",
"chrome_service_name.cc",
"chrome_service_name.h",
+ "crostini/crostini_registry_service.cc",
+ "crostini/crostini_registry_service.h",
+ "crostini/crostini_registry_service_factory.cc",
+ "crostini/crostini_registry_service_factory.h",
"cryptauth/chrome_cryptauth_service.cc",
"cryptauth/chrome_cryptauth_service.h",
"cryptauth/chrome_cryptauth_service_factory.cc",
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.cc b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
new file mode 100644
index 0000000..94b3b24
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
@@ -0,0 +1,79 @@
+// Copyright 2018 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/crostini/crostini_registry_service.h"
+
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/crx_file/id_util.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
+
+namespace chromeos {
+
+namespace {
+
+constexpr char kCrostiniRegistryPref[] = "crostini.registry";
+
+constexpr char kAppDesktopFileIdKey[] = "desktop_file_id";
+constexpr char kAppNameKey[] = "name";
+
+constexpr char kCrostiniAppIdPrefix[] = "crostini:";
+
+std::string GenerateAppId(const std::string& desktop_file_id) {
+ return crx_file::id_util::GenerateId(kCrostiniAppIdPrefix + desktop_file_id);
+}
+
+} // namespace
+
+CrostiniRegistryService::Registration::Registration(
+ const std::string& desktop_file_id,
+ const std::string& name)
+ : desktop_file_id(desktop_file_id), name(name) {}
+
+CrostiniRegistryService::CrostiniRegistryService(Profile* profile)
+ : prefs_(profile->GetPrefs()) {}
+
+CrostiniRegistryService::~CrostiniRegistryService() = default;
+
+std::unique_ptr<CrostiniRegistryService::Registration>
+CrostiniRegistryService::GetRegistration(const std::string& app_id) const {
+ DCHECK(crx_file::id_util::IdIsValid(app_id));
+ const base::DictionaryValue* apps =
+ prefs_->GetDictionary(kCrostiniRegistryPref);
+ const base::Value* pref_registration =
+ apps->FindKeyOfType(app_id, base::Value::Type::DICTIONARY);
+ if (!pref_registration)
+ return nullptr;
+
+ const base::Value* desktop_file_id = pref_registration->FindKeyOfType(
+ kAppDesktopFileIdKey, base::Value::Type::STRING);
+ const base::Value* name =
+ pref_registration->FindKeyOfType(kAppNameKey, base::Value::Type::STRING);
+
+ return std::make_unique<Registration>(desktop_file_id->GetString(),
+ name->GetString());
+}
+
+void CrostiniRegistryService::SetRegistration(
+ const Registration& registration) {
+ base::Value pref_registration(base::Value::Type::DICTIONARY);
+ pref_registration.SetKey(kAppDesktopFileIdKey,
+ base::Value(registration.desktop_file_id));
+ pref_registration.SetKey(kAppNameKey, base::Value(registration.name));
+
+ DictionaryPrefUpdate update(prefs_, kCrostiniRegistryPref);
+ base::DictionaryValue* apps = update.Get();
+ apps->SetKey(GenerateAppId(registration.desktop_file_id),
+ std::move(pref_registration));
+}
+
+// static
+void CrostiniRegistryService::RegisterProfilePrefs(
+ PrefRegistrySimple* registry) {
+ registry->RegisterDictionaryPref(kCrostiniRegistryPref);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.h b/chrome/browser/chromeos/crostini/crostini_registry_service.h
new file mode 100644
index 0000000..f8459d5
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.h
@@ -0,0 +1,64 @@
+// Copyright 2018 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_REGISTRY_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_REGISTRY_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class Profile;
+class PrefRegistrySimple;
+class PrefService;
+
+namespace chromeos {
+
+// The CrostiniRegistryService stores information about Desktop Entries (apps)
+// in Crostini. We store this in prefs so that it is readily available even when
+// the VM isn't running. The registrations here correspond to .desktop files,
+// which are detailed in the spec:
+// https://www.freedesktop.org/wiki/Specifications/desktop-entry-spec/
+class CrostiniRegistryService : public KeyedService {
+ public:
+ struct Registration {
+ Registration(const std::string& desktop_file_id, const std::string& name);
+ ~Registration() = default;
+
+ std::string desktop_file_id;
+
+ // TODO(timloh): Add other relevant fields from the Desktop Entry Spec, in
+ // particular: Icon, Comment, MimeType, NoDisplay
+ // TODO(timloh): .desktop files allow localization of this string. We need
+ // to expand this to support those too.
+ std::string name;
+
+ DISALLOW_COPY_AND_ASSIGN(Registration);
+ };
+
+ explicit CrostiniRegistryService(Profile* profile);
+ ~CrostiniRegistryService() override;
+
+ // Return null if |app_id| is not found in the registry.
+ std::unique_ptr<CrostiniRegistryService::Registration> GetRegistration(
+ const std::string& app_id) const;
+
+ // If an app has already been registered for the given desktop file id, it
+ // will be overriden.
+ void SetRegistration(const Registration& registration);
+
+ static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
+ private:
+ // Owned by the BrowserContext.
+ PrefService* const prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrostiniRegistryService);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_REGISTRY_SERVICE_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service_factory.cc b/chrome/browser/chromeos/crostini/crostini_registry_service_factory.cc
new file mode 100644
index 0000000..4b508c5
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service_factory.cc
@@ -0,0 +1,39 @@
+// Copyright 2018 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/crostini/crostini_registry_service_factory.h"
+
+#include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+namespace chromeos {
+
+// static
+CrostiniRegistryService* CrostiniRegistryServiceFactory::GetForProfile(
+ Profile* profile) {
+ return static_cast<CrostiniRegistryService*>(
+ GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+CrostiniRegistryServiceFactory* CrostiniRegistryServiceFactory::GetInstance() {
+ static base::NoDestructor<CrostiniRegistryServiceFactory> factory;
+ return factory.get();
+}
+
+CrostiniRegistryServiceFactory::CrostiniRegistryServiceFactory()
+ : BrowserContextKeyedServiceFactory(
+ "CrostiniRegistryService",
+ BrowserContextDependencyManager::GetInstance()) {}
+
+CrostiniRegistryServiceFactory::~CrostiniRegistryServiceFactory() = default;
+
+KeyedService* CrostiniRegistryServiceFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ Profile* profile = Profile::FromBrowserContext(context);
+ return new CrostiniRegistryService(profile);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service_factory.h b/chrome/browser/chromeos/crostini/crostini_registry_service_factory.h
new file mode 100644
index 0000000..0db9852
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service_factory.h
@@ -0,0 +1,39 @@
+// Copyright 2018 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_REGISTRY_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_REGISTRY_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+
+namespace chromeos {
+
+class CrostiniRegistryService;
+
+class CrostiniRegistryServiceFactory
+ : public BrowserContextKeyedServiceFactory {
+ public:
+ static CrostiniRegistryService* GetForProfile(Profile* profile);
+ static CrostiniRegistryServiceFactory* GetInstance();
+
+ private:
+ friend class base::NoDestructor<CrostiniRegistryServiceFactory>;
+
+ CrostiniRegistryServiceFactory();
+ ~CrostiniRegistryServiceFactory() override;
+
+ // BrowserContextKeyedServiceFactory:
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(CrostiniRegistryServiceFactory);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_REGISTRY_SERVICE_FACTORY_H_
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc b/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
new file mode 100644
index 0000000..e1b306b
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2018 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/crostini/crostini_registry_service.h"
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/crx_file/id_util.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+class CrostiniRegistryServiceTest : public testing::Test {
+ public:
+ CrostiniRegistryServiceTest() = default;
+
+ // testing::Test:
+ void SetUp() override {
+ service_ = std::make_unique<CrostiniRegistryService>(&profile_);
+ }
+
+ protected:
+ std::string GenerateAppId(const std::string& desktop_file_id) {
+ return crx_file::id_util::GenerateId("crostini:" + desktop_file_id);
+ }
+
+ CrostiniRegistryService* service() { return service_.get(); }
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfile profile_;
+
+ std::unique_ptr<CrostiniRegistryService> service_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrostiniRegistryServiceTest);
+};
+
+TEST_F(CrostiniRegistryServiceTest, SetAndGetRegistration) {
+ std::string desktop_file_id = "vim";
+ std::string name = "Vim";
+ std::string app_id = GenerateAppId(desktop_file_id);
+ EXPECT_EQ(nullptr, service()->GetRegistration(app_id));
+
+ CrostiniRegistryService::Registration registration(desktop_file_id, name);
+ service()->SetRegistration(registration);
+ auto result = service()->GetRegistration(app_id);
+ ASSERT_NE(nullptr, result);
+ EXPECT_EQ(result->desktop_file_id, desktop_file_id);
+ EXPECT_EQ(result->name, name);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 91b4eb6..3bc58b8 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -201,6 +201,7 @@
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/arc/arc_session_manager.h"
#include "chrome/browser/chromeos/arc/policy/arc_policy_bridge.h"
+#include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
#include "chrome/browser/chromeos/customization/customization_document.h"
#include "chrome/browser/chromeos/display/display_prefs.h"
#include "chrome/browser/chromeos/extensions/echo_private_api.h"
@@ -622,6 +623,7 @@
#if defined(OS_CHROMEOS)
arc::prefs::RegisterProfilePrefs(registry);
+ chromeos::CrostiniRegistryService::RegisterProfilePrefs(registry);
chromeos::first_run::RegisterProfilePrefs(registry);
chromeos::file_system_provider::RegisterProfilePrefs(registry);
chromeos::KeyPermissions::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 73dcd17..2b85d00 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -99,6 +99,7 @@
#endif
#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
#include "chrome/browser/chromeos/cryptauth/chrome_cryptauth_service_factory.h"
#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
#include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
@@ -251,6 +252,7 @@
EnhancedBookmarkKeyServiceFactory::GetInstance();
#endif
#if defined(OS_CHROMEOS)
+ chromeos::CrostiniRegistryServiceFactory::GetInstance();
chromeos::CupsPrintJobManagerFactory::GetInstance();
chromeos::SyncedPrintersManagerFactory::GetInstance();
chromeos::smb_client::SmbServiceFactory::GetInstance();
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 13e0140..b6f073f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3164,6 +3164,7 @@
"../browser/ui/window_sizer/window_sizer_unittest.cc",
]
sources += [
+ "../browser/chromeos/crostini/crostini_registry_service_unittest.cc",
"../browser/chromeos/policy/policy_cert_verifier_unittest.cc",
"../browser/component_updater/cros_component_installer_unittest.cc",
"../browser/mash_service_registry_unittest.cc",