diff --git a/shell/platform/windows/uwptool_main.cc b/shell/platform/windows/uwptool_main.cc index 24bc41981d4c7..8a18711855aae 100644 --- a/shell/platform/windows/uwptool_main.cc +++ b/shell/platform/windows/uwptool_main.cc @@ -20,13 +20,12 @@ namespace { // Prints a list of installed UWP apps to stdout. void PrintInstalledApps() { flutter::ApplicationStore app_store; - for (const flutter::Application& app : app_store.GetInstalledApplications()) { + for (const flutter::Application& app : app_store.GetApps()) { std::wcout << app.GetPackageFamily() << std::endl; } } -// Launches the app installed on the system whose Application User Model ID is -// prefixed with app_id, with the specified arguments list. +// Launches the app installed on the system with the specified package. // // Returns -1 if no matching app, or multiple matching apps are found, or if // the app fails to launch. Otherwise, the process ID of the launched app is @@ -34,20 +33,41 @@ void PrintInstalledApps() { int LaunchApp(const std::wstring_view package_family, const std::wstring_view args) { flutter::ApplicationStore app_store; - std::optional app = - app_store.GetInstalledApplication(package_family); - if (!app) { - return -1; + for (flutter::Application& app : app_store.GetApps(package_family)) { + int process_id = app.Launch(args); + if (process_id != -1) { + return process_id; + } } - return app->Launch(args); + return -1; +} + +// Uninstalls the app with the specified package. +// +// Returns true on success. +bool UninstallApp(const std::wstring_view package_family) { + bool success = true; + flutter::ApplicationStore app_store; + for (flutter::Application& app : app_store.GetApps(package_family)) { + if (app.Uninstall()) { + std::wcerr << L"Uninstalled application " << app.GetPackageFullName() + << std::endl; + } else { + std::wcerr << L"error: Failed to uninstall application " + << app.GetPackageFullName() << std::endl; + success = false; + } + } + return success; } // Prints the command usage to stderr. void PrintUsage() { - std::cerr << "usage: uwptool COMMAND [APP_ID]" << std::endl; + std::cerr << "usage: uwptool COMMAND [ARGUMENTS]" << std::endl; std::cerr << "commands:" << std::endl; - std::cerr << " listapps list installed applications" << std::endl; - std::cerr << " launch launch an application" << std::endl; + std::cerr << " listapps list installed apps" << std::endl; + std::cerr << " launch PACKAGE_FAMILY launch an app" << std::endl; + std::cerr << " uninstall PACKAGE_FAMILY uninstall an app" << std::endl; } } // namespace @@ -67,12 +87,12 @@ int main(int argc, char** argv) { PrintInstalledApps(); return 0; } else if (command == "launch") { - if (command_line.positional_args().size() < 1) { + if (args.size() < 2) { PrintUsage(); return 1; } - // Get the package family. + // Get the package family name. std::string package_family = args[1]; // Concatenate the remaining args, comma-separated. @@ -97,6 +117,13 @@ int main(int argc, char** argv) { // Write the PID to stdout. The flutter tool reads this value in. std::cout << process_id << std::endl; return 0; + } else if (command == "uninstall") { + if (args.size() < 2) { + PrintUsage(); + return 1; + } + std::string package_family = args[1]; + return UninstallApp(flutter::Utf16FromUtf8(package_family)) ? 0 : 1; } std::cerr << "Unknown command: " << command << std::endl; diff --git a/shell/platform/windows/uwptool_utils.cc b/shell/platform/windows/uwptool_utils.cc index 2fa58ed25e415..ebb0bfa268686 100644 --- a/shell/platform/windows/uwptool_utils.cc +++ b/shell/platform/windows/uwptool_utils.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -19,9 +20,12 @@ namespace flutter { -Application::Application(const std::wstring_view package_family, +Application::Application(const std::wstring_view package_name, + const std::wstring_view package_family, const std::wstring_view package_full_name) - : package_family_(package_family), package_full_name_(package_full_name) {} + : package_name_(package_name), + package_family_(package_family), + package_full_name_(package_full_name) {} int Application::Launch(const std::wstring_view args) { // Create the ApplicationActivationManager. @@ -45,34 +49,67 @@ int Application::Launch(const std::wstring_view args) { return process_id; } -std::vector ApplicationStore::GetInstalledApplications() const { +bool Application::Uninstall() { + using winrt::Windows::Foundation::AsyncStatus; + using winrt::Windows::Management::Deployment::PackageManager; + using winrt::Windows::Management::Deployment::RemovalOptions; + + PackageManager package_manager; + auto operation = package_manager.RemovePackageAsync( + package_full_name_, RemovalOptions::RemoveForAllUsers); + operation.get(); + + if (operation.Status() == AsyncStatus::Completed) { + return true; + } else if (operation.Status() == AsyncStatus::Canceled) { + return false; + } else if (operation.Status() == AsyncStatus::Error) { + auto result = operation.GetResults(); + std::wcerr << L"error: uninstall failed for package " << package_full_name_ + << L" with error: " << result.ErrorText().c_str() << std::endl; + return false; + } + return false; +} + +std::vector ApplicationStore::GetApps() const { using winrt::Windows::ApplicationModel::Package; using winrt::Windows::Management::Deployment::PackageManager; // Find packages for the current user (default for empty string). - PackageManager package_manager; std::vector apps; - for (const Package& package : package_manager.FindPackagesForUser(L"")) { - apps.emplace_back(package.Id().FamilyName().c_str(), - package.Id().FullName().c_str()); + try { + PackageManager package_manager; + for (const Package& package : package_manager.FindPackagesForUser(L"")) { + apps.emplace_back(package.Id().Name().c_str(), + package.Id().FamilyName().c_str(), + package.Id().FullName().c_str()); + } + } catch (winrt::hresult_error error) { + return {}; } return apps; } -std::optional ApplicationStore::GetInstalledApplication( +std::vector ApplicationStore::GetApps( const std::wstring_view package_family) const { using winrt::Windows::ApplicationModel::Package; using winrt::Windows::Management::Deployment::PackageManager; // Find packages for the current user (default for empty string). - PackageManager package_manager; std::vector apps; - for (const Package& package : - package_manager.FindPackagesForUser(L"", package_family)) { - return std::optional(Application(package.Id().FamilyName().c_str(), - package.Id().FullName().c_str())); + try { + PackageManager package_manager; + for (const Package& package : + package_manager.FindPackagesForUser(L"", package_family)) { + apps.emplace_back(package.Id().Name().c_str(), + package.Id().FamilyName().c_str(), + package.Id().FullName().c_str()); + } + } catch (winrt::hresult_error error) { + return {}; } - return std::nullopt; + return apps; } } // namespace flutter diff --git a/shell/platform/windows/uwptool_utils.h b/shell/platform/windows/uwptool_utils.h index f2789871c1fc1..08691d6ab1fee 100644 --- a/shell/platform/windows/uwptool_utils.h +++ b/shell/platform/windows/uwptool_utils.h @@ -14,15 +14,29 @@ namespace flutter { // A UWP application. class Application { public: - explicit Application(const std::wstring_view package_family, + explicit Application(const std::wstring_view package_name, + const std::wstring_view package_family, const std::wstring_view package_full_name); Application(const Application& other) = default; Application& operator=(const Application& other) = default; + // Returns the package name. + // + // The package name is a globally unique name that represents the 'friendly' + // name of a package. + std::wstring GetPackageName() const { return package_name_; } + // Returns the package family. + // + // The package family is a serialized form of the package identifiers that + // includes the package name and publisher. std::wstring GetPackageFamily() const { return package_family_; } // Returns the package full name. + // + // The package full name is a serialized form of the package identifiers that + // includes a particular version of the package on the computer. It encodes + // package name, publisher, architecture and version information. std::wstring GetPackageFullName() const { return package_full_name_; } // Launches the application with the specified list of launch arguments. @@ -30,7 +44,13 @@ class Application { // Returns the process ID on success, or -1 on failure. int Launch(const std::wstring_view args); + // Uninstalls the application. + // + // Returns true on success. + bool Uninstall(); + private: + std::wstring package_name_; std::wstring package_family_; std::wstring package_full_name_; }; @@ -44,11 +64,11 @@ class ApplicationStore { ApplicationStore(const ApplicationStore& other) = delete; ApplicationStore& operator=(const ApplicationStore& other) = delete; - // Returns a list of all installed application user model IDs. - std::vector GetInstalledApplications() const; + // Returns all installed applications. + std::vector GetApps() const; - // Returns a list of all installed application user model IDs. - std::optional GetInstalledApplication( + // Returns all installed applications with the specified family name. + std::vector GetApps( const std::wstring_view package_family) const; }; diff --git a/shell/platform/windows/uwptool_utils_unittests.cc b/shell/platform/windows/uwptool_utils_unittests.cc index 5589cdfd43c21..151fbbaf33a4a 100644 --- a/shell/platform/windows/uwptool_utils_unittests.cc +++ b/shell/platform/windows/uwptool_utils_unittests.cc @@ -20,9 +20,9 @@ namespace testing { // Verify that at least one Microsoft app (e.g. Microsoft.WindowsCalculator) is // installed and can be found. -TEST(ApplicationStore, GetInstalledApplications) { - ApplicationStore app_store; - std::vector apps = app_store.GetInstalledApplications(); +TEST(ApplicationStore, GetApps) { + ApplicationStore store; + std::vector apps = store.GetApps(); EXPECT_FALSE(apps.empty()); auto ms_pos = std::find_if(apps.begin(), apps.end(), [](const auto& app) { @@ -32,16 +32,18 @@ TEST(ApplicationStore, GetInstalledApplications) { } // Verify that we can look up an app by family name. -TEST(ApplicationStore, GetInstalledApplication) { - ApplicationStore app_store; - std::vector apps = app_store.GetInstalledApplications(); - EXPECT_FALSE(apps.empty()); - - std::optional found_app = - app_store.GetInstalledApplication(apps[0].GetPackageFamily()); - ASSERT_TRUE(found_app != std::nullopt); - EXPECT_EQ(found_app->GetPackageFamily(), apps[0].GetPackageFamily()); - EXPECT_EQ(found_app->GetPackageFullName(), apps[0].GetPackageFullName()); +TEST(ApplicationStore, GetAppsByPackageFamily) { + ApplicationStore store; + std::vector all_apps = store.GetApps(); + EXPECT_FALSE(all_apps.empty()); + + Application app = all_apps[0]; + std::vector found_apps = store.GetApps(app.GetPackageFamily()); + ASSERT_FALSE(found_apps.empty()); + for (const Application& found_app : found_apps) { + EXPECT_EQ(found_app.GetPackageName(), app.GetPackageName()); + EXPECT_EQ(found_app.GetPackageFamily(), app.GetPackageFamily()); + } } } // namespace testing