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