From 8d25cf4cc74bac3b1e755bd28a81ca6663699f9d Mon Sep 17 00:00:00 2001 From: JohnMcPMS Date: Fri, 29 Sep 2023 13:28:32 -0700 Subject: [PATCH] Enable COM API access to correlate with the tracking database only (#3703) (#3710) This change adds a flag that lets the caller specify that they are only interested in the installed tracking data, not the available packages. With it set, one can correlate to the catalog via the tracking database without ever needing to access the remote source information. --- .../CompositeSource.cpp | 4 - .../Public/winget/RepositorySource.h | 8 ++ .../RepositorySource.cpp | 126 ++++++++++++++---- .../PackageCatalogReference.cpp | 24 ++++ .../PackageCatalogReference.h | 9 +- .../PackageManager.idl | 9 +- 6 files changed, 147 insertions(+), 33 deletions(-) diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index aed522316b..a052de76b0 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -1412,15 +1412,12 @@ namespace AppInstaller::Repository } } - bool addedAvailablePackage = false; - // Directly search for the available package from tracking information. if (trackingPackage) { auto availablePackage = GetTrackedPackageFromAvailableSource(result, trackedSource, trackingPackage->GetProperty(PackageProperty::Id)); if (availablePackage) { - addedAvailablePackage = true; compositePackage->AddAvailablePackage(std::move(availablePackage)); } compositePackage->SetTracking(std::move(trackedSource), std::move(trackingPackage), std::move(trackingPackageVersion)); @@ -1453,7 +1450,6 @@ namespace AppInstaller::Repository }); // For non pinning cases. We found some matching packages here, don't keep going. - addedAvailablePackage = true; compositePackage->AddAvailablePackage(std::move(availablePackage)); } } diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h index 1bba6979ff..4e8d882830 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h @@ -225,6 +225,13 @@ namespace AppInstaller::Repository // Set background update check interval. void SetBackgroundUpdateInterval(TimeSpan interval); + // Indicates that we are only interested in the PackageTrackingCatalog for the source. + // Must be set before Open to have effect, and will prevent the underlying source from being updated or opened. + void InstalledPackageInformationOnly(bool value); + + // Determines if this source refers to the given well known source. + bool IsWellKnownSource(WellKnownSource wellKnownSource); + // Execute a search on the source. SearchResult Search(const SearchRequest& request) const; @@ -287,6 +294,7 @@ namespace AppInstaller::Repository bool m_isSourceToBeAdded = false; bool m_isComposite = false; std::optional m_backgroundUpdateInterval; + bool m_installedPackageInformationOnly = false; mutable PackageTrackingCatalog m_trackingCatalog; }; } diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp index 013976ee2b..a99e954181 100644 --- a/src/AppInstallerRepositoryCore/RepositorySource.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -202,6 +202,53 @@ namespace AppInstaller::Repository SourceDetails m_details; std::exception_ptr m_exception; }; + + // A wrapper that doesn't actually forward the search requests. + struct TrackingOnlySourceWrapper : public ISource + { + TrackingOnlySourceWrapper(std::shared_ptr wrapped) : m_wrapped(std::move(wrapped)) + { + m_identifier = m_wrapped->GetIdentifier(); + } + + const std::string& GetIdentifier() const override { return m_identifier; } + + SourceDetails& GetDetails() const override { return m_wrapped->GetDetails(); } + + SourceInformation GetInformation() const override { return m_wrapped->GetInformation(); } + + SearchResult Search(const SearchRequest&) const override { return {}; } + + void* CastTo(ISourceType) override { return nullptr; } + + private: + std::shared_ptr m_wrapped; + std::string m_identifier; + }; + + // A wrapper to create another wrapper. + struct TrackingOnlyReferenceWrapper : public ISourceReference + { + TrackingOnlyReferenceWrapper(std::shared_ptr wrapped) : m_wrapped(std::move(wrapped)) {} + + std::string GetIdentifier() override { return m_wrapped->GetIdentifier(); } + + SourceDetails& GetDetails() override { return m_wrapped->GetDetails(); } + + SourceInformation GetInformation() override { return m_wrapped->GetInformation(); } + + bool SetCustomHeader(std::optional) override { return false; } + + void SetCaller(std::string caller) override { m_wrapped->SetCaller(std::move(caller)); } + + std::shared_ptr Open(IProgressCallback&) override + { + return std::make_shared(m_wrapped); + } + + private: + std::shared_ptr m_wrapped; + }; } std::unique_ptr ISourceFactory::GetForType(std::string_view type) @@ -475,6 +522,18 @@ namespace AppInstaller::Repository m_backgroundUpdateInterval = interval; } + void Source::InstalledPackageInformationOnly(bool value) + { + m_installedPackageInformationOnly = value; + } + + bool Source::IsWellKnownSource(WellKnownSource wellKnownSource) + { + SourceDetails details = GetDetails(); + auto wellKnown = CheckForWellKnownSourceMatch(details.Name, details.Arg, details.Type); + return wellKnown && wellKnown.value() == wellKnownSource; + } + SearchResult Source::Search(const SearchRequest& request) const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_source); @@ -554,51 +613,68 @@ namespace AppInstaller::Repository if (!m_source) { + std::vector>* sourceReferencesToOpen = nullptr; + std::vector> sourceReferencesForTrackingOnly; std::unique_ptr sourceList; - // Check for updates before opening. - for (auto& sourceReference : m_sourceReferences) + if (m_installedPackageInformationOnly) { - auto& details = sourceReference->GetDetails(); - if (ShouldUpdateBeforeOpen(details, m_backgroundUpdateInterval)) + sourceReferencesToOpen = &sourceReferencesForTrackingOnly; + + // Create a wrapper for each reference + for (auto& sourceReference : m_sourceReferences) { - try + sourceReferencesForTrackingOnly.emplace_back(std::make_shared(sourceReference)); + } + } + else + { + // Check for updates before opening. + for (auto& sourceReference : m_sourceReferences) + { + auto& details = sourceReference->GetDetails(); + if (ShouldUpdateBeforeOpen(details, m_backgroundUpdateInterval)) { - // TODO: Consider adding a context callback to indicate we are doing the same action - // to avoid the progress bar fill up multiple times. - if (BackgroundUpdateSourceFromDetails(details, progress)) + try { - if (sourceList == nullptr) + // TODO: Consider adding a context callback to indicate we are doing the same action + // to avoid the progress bar fill up multiple times. + if (BackgroundUpdateSourceFromDetails(details, progress)) { - sourceList = std::make_unique(); + if (sourceList == nullptr) + { + sourceList = std::make_unique(); + } + + auto detailsInternal = sourceList->GetSource(details.Name); + detailsInternal->LastUpdateTime = details.LastUpdateTime; + sourceList->SaveMetadata(*detailsInternal); + } + else + { + AICLI_LOG(Repo, Error, << "Failed to update source: " << details.Name); + result.emplace_back(details); } - - auto detailsInternal = sourceList->GetSource(details.Name); - detailsInternal->LastUpdateTime = details.LastUpdateTime; - sourceList->SaveMetadata(*detailsInternal); } - else + catch (...) { - AICLI_LOG(Repo, Error, << "Failed to update source: " << details.Name); + LOG_CAUGHT_EXCEPTION(); + AICLI_LOG(Repo, Warning, << "Failed to update source: " << details.Name); result.emplace_back(details); } } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - AICLI_LOG(Repo, Warning, << "Failed to update source: " << details.Name); - result.emplace_back(details); - } } + + sourceReferencesToOpen = &m_sourceReferences; } - if (m_sourceReferences.size() > 1) + if (sourceReferencesToOpen->size() > 1) { AICLI_LOG(Repo, Info, << "Multiple sources available, creating aggregated source."); auto aggregatedSource = std::make_shared("*DefaultSource"); std::vector> openExceptionProxies; - for (auto& sourceReference : m_sourceReferences) + for (auto& sourceReference : *sourceReferencesToOpen) { AICLI_LOG(Repo, Info, << "Adding to aggregated source: " << sourceReference->GetDetails().Name); @@ -628,7 +704,7 @@ namespace AppInstaller::Repository } else { - m_source = m_sourceReferences[0]->Open(progress); + m_source = (*sourceReferencesToOpen)[0]->Open(progress); } } diff --git a/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp b/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp index dcaff3ef87..43a9255547 100644 --- a/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp +++ b/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp @@ -28,6 +28,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation if (IsBackgroundProcessForPolicy()) { + // Delay the default update interval for these background processes static constexpr winrt::Windows::Foundation::TimeSpan s_PackageCatalogUpdateIntervalDelay_Base = 168h; //1 week // Add a bit of randomness to the default interval time @@ -35,12 +36,17 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::uniform_int_distribution distribution(0, 604800); m_packageCatalogBackgroundUpdateInterval = s_PackageCatalogUpdateIntervalDelay_Base + std::chrono::seconds(distribution(randomEngine)); + + // Prevent any update / data processing by default for these background processes for now + m_installedPackageInformationOnly = m_sourceReference.IsWellKnownSource(AppInstaller::Repository::WellKnownSource::WinGet); } } + void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions options) { m_compositePackageCatalogOptions = options; } + bool PackageCatalogReference::IsComposite() { return (m_compositePackageCatalogOptions != nullptr); @@ -95,6 +101,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation auto copy = catalogImpl->m_sourceReference; copy.SetCaller(callerName); copy.SetBackgroundUpdateInterval(catalog.PackageCatalogBackgroundUpdateInterval()); + copy.InstalledPackageInformationOnly(catalog.InstalledPackageInformationOnly()); copy.Open(progress); remoteSources.emplace_back(std::move(copy)); } @@ -137,6 +144,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation source = m_sourceReference; source.SetCaller(callerName); source.SetBackgroundUpdateInterval(PackageCatalogBackgroundUpdateInterval()); + source.InstalledPackageInformationOnly(m_installedPackageInformationOnly); source.Open(progress); } @@ -216,6 +224,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_acceptSourceAgreements; } + void PackageCatalogReference::PackageCatalogBackgroundUpdateInterval(winrt::Windows::Foundation::TimeSpan const& value) { if (IsComposite()) @@ -229,4 +238,19 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_packageCatalogBackgroundUpdateInterval; } + + bool PackageCatalogReference::InstalledPackageInformationOnly() + { + return m_installedPackageInformationOnly; + } + + void PackageCatalogReference::InstalledPackageInformationOnly(bool value) + { + if (IsComposite()) + { + throw winrt::hresult_illegal_state_change(); + } + + m_installedPackageInformationOnly = value; + } } diff --git a/src/Microsoft.Management.Deployment/PackageCatalogReference.h b/src/Microsoft.Management.Deployment/PackageCatalogReference.h index 966dc055d4..ab18dbb655 100644 --- a/src/Microsoft.Management.Deployment/PackageCatalogReference.h +++ b/src/Microsoft.Management.Deployment/PackageCatalogReference.h @@ -11,8 +11,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation PackageCatalogReference() = default; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) - void Initialize(winrt::Microsoft::Management::Deployment::PackageCatalogInfo packageCatalogInfo, ::AppInstaller::Repository::Source sourceReference); - void Initialize(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions options); + void Initialize(Deployment::PackageCatalogInfo packageCatalogInfo, ::AppInstaller::Repository::Source sourceReference); + void Initialize(Deployment::CreateCompositePackageCatalogOptions options); #endif bool IsComposite(); @@ -22,12 +22,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Windows::Foundation::Collections::IVectorView SourceAgreements(); hstring AdditionalPackageCatalogArguments(); void AdditionalPackageCatalogArguments(hstring const& value); - // Contract 6.0 + // Contract 6 bool AcceptSourceAgreements(); void AcceptSourceAgreements(bool value); // Contract 8.0 winrt::Windows::Foundation::TimeSpan PackageCatalogBackgroundUpdateInterval(); void PackageCatalogBackgroundUpdateInterval(winrt::Windows::Foundation::TimeSpan const& value); + bool InstalledPackageInformationOnly(); + void InstalledPackageInformationOnly(bool value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: @@ -37,6 +39,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation ::AppInstaller::Repository::Source m_sourceReference; std::optional m_additionalPackageCatalogArguments; bool m_acceptSourceAgreements = true; + bool m_installedPackageInformationOnly = false; std::once_flag m_sourceAgreementsOnceFlag; winrt::Windows::Foundation::TimeSpan m_packageCatalogBackgroundUpdateInterval = winrt::Windows::Foundation::TimeSpan::zero(); #endif diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index cff8749b81..ba706b9d42 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -2,7 +2,7 @@ // Licensed under the MIT License. namespace Microsoft.Management.Deployment { - [contractversion(8)] + [contractversion(9)] apicontract WindowsPackageManagerContract{}; /// State of the install @@ -755,6 +755,13 @@ namespace Microsoft.Management.Deployment /// Time interval for package catalog to check for an update. Setting to zero will disable the check for update. Windows.Foundation.TimeSpan PackageCatalogBackgroundUpdateInterval; } + + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 9)] + { + // When set to true, the opened catalog will only provide the information regarding packages installed from this catalog. + // In this mode, no external resources should be required. + Boolean InstalledPackageInformationOnly; + } } /// Catalogs with PackageCatalogOrigin Predefined