Skip to content

Commit

Permalink
Enable COM API access to correlate with the tracking database only (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
JohnMcPMS authored Sep 29, 2023
1 parent aa15b54 commit 8d25cf4
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 33 deletions.
4 changes: 0 additions & 4 deletions src/AppInstallerRepositoryCore/CompositeSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -287,6 +294,7 @@ namespace AppInstaller::Repository
bool m_isSourceToBeAdded = false;
bool m_isComposite = false;
std::optional<TimeSpan> m_backgroundUpdateInterval;
bool m_installedPackageInformationOnly = false;
mutable PackageTrackingCatalog m_trackingCatalog;
};
}
126 changes: 101 additions & 25 deletions src/AppInstallerRepositoryCore/RepositorySource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ISourceReference> 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<ISourceReference> m_wrapped;
std::string m_identifier;
};

// A wrapper to create another wrapper.
struct TrackingOnlyReferenceWrapper : public ISourceReference
{
TrackingOnlyReferenceWrapper(std::shared_ptr<ISourceReference> 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<std::string>) override { return false; }

void SetCaller(std::string caller) override { m_wrapped->SetCaller(std::move(caller)); }

std::shared_ptr<ISource> Open(IProgressCallback&) override
{
return std::make_shared<TrackingOnlySourceWrapper>(m_wrapped);
}

private:
std::shared_ptr<ISourceReference> m_wrapped;
};
}

std::unique_ptr<ISourceFactory> ISourceFactory::GetForType(std::string_view type)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -554,51 +613,68 @@ namespace AppInstaller::Repository

if (!m_source)
{
std::vector<std::shared_ptr<ISourceReference>>* sourceReferencesToOpen = nullptr;
std::vector<std::shared_ptr<ISourceReference>> sourceReferencesForTrackingOnly;
std::unique_ptr<SourceList> 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<TrackingOnlyReferenceWrapper>(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<SourceList>();
if (sourceList == nullptr)
{
sourceList = std::make_unique<SourceList>();
}

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<CompositeSource>("*DefaultSource");
std::vector<std::shared_ptr<OpenExceptionProxy>> openExceptionProxies;

for (auto& sourceReference : m_sourceReferences)
for (auto& sourceReference : *sourceReferencesToOpen)
{
AICLI_LOG(Repo, Info, << "Adding to aggregated source: " << sourceReference->GetDetails().Name);

Expand Down Expand Up @@ -628,7 +704,7 @@ namespace AppInstaller::Repository
}
else
{
m_source = m_sourceReferences[0]->Open(progress);
m_source = (*sourceReferencesToOpen)[0]->Open(progress);
}
}

Expand Down
24 changes: 24 additions & 0 deletions src/Microsoft.Management.Deployment/PackageCatalogReference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,25 @@ 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
std::default_random_engine randomEngine(std::random_device{}());
std::uniform_int_distribution<long long> 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);
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -216,6 +224,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation
{
return m_acceptSourceAgreements;
}

void PackageCatalogReference::PackageCatalogBackgroundUpdateInterval(winrt::Windows::Foundation::TimeSpan const& value)
{
if (IsComposite())
Expand All @@ -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;
}
}
9 changes: 6 additions & 3 deletions src/Microsoft.Management.Deployment/PackageCatalogReference.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -22,12 +22,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation
winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::SourceAgreement> 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:
Expand All @@ -37,6 +39,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation
::AppInstaller::Repository::Source m_sourceReference;
std::optional<std::string> 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
Expand Down
9 changes: 8 additions & 1 deletion src/Microsoft.Management.Deployment/PackageManager.idl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.
namespace Microsoft.Management.Deployment
{
[contractversion(8)]
[contractversion(9)]
apicontract WindowsPackageManagerContract{};

/// State of the install
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 8d25cf4

Please sign in to comment.