winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 804c1252382cd0435f9fde68b192d2012516c3d0
parent 657d33ce1702fc11f1fa64ca46c7dd6c07bd12ac
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date:   Mon, 25 Sep 2023 20:54:12 -0700

Log Com invocation startup telemetry and delay auto update time when invoked from explorer (#3665)

- Add telemetry event for PackageManager class creation
- Delay source update time to 7 days by default when invoked from explorer
- Also fixes Source agreements related crash found when doing the above work
Diffstat:
M.github/actions/spelling/expect.txt | 1+
Msrc/AppInstallerCommonCore/AppInstallerTelemetry.cpp | 5+++++
Msrc/AppInstallerCommonCore/Public/AppInstallerTelemetry.h | 2++
Msrc/AppInstallerRepositoryCore/Public/winget/RepositorySource.h | 7+++++++
Msrc/AppInstallerRepositoryCore/RepositorySource.cpp | 37+++++++++++++++++++++++++++++--------
Msrc/Microsoft.Management.Deployment/Helpers.cpp | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Microsoft.Management.Deployment/Helpers.h | 2++
Msrc/Microsoft.Management.Deployment/PackageCatalogReference.cpp | 88+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/Microsoft.Management.Deployment/PackageCatalogReference.h | 4++++
Msrc/Microsoft.Management.Deployment/PackageManager.cpp | 11+++++++++++
Msrc/Microsoft.Management.Deployment/PackageManager.h | 8+++++++-
Msrc/Microsoft.Management.Deployment/PackageManager.idl | 8+++++++-
Msrc/Microsoft.Management.Deployment/pch.h | 5+++--
13 files changed, 180 insertions(+), 48 deletions(-)

diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt @@ -454,6 +454,7 @@ Syncy sysrefcomp systemnotsupported Tagit +taskhostw TCpp tcs TEMPDIRECTORY diff --git a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp @@ -173,6 +173,11 @@ namespace AppInstaller::Logging m_executionStage = stage; } + void TelemetryTraceLogger::SetUseSummary(bool useSummary) noexcept + { + m_useSummary = useSummary; + } + std::unique_ptr<TelemetryTraceLogger> TelemetryTraceLogger::CreateSubTraceLogger() const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !this->m_isInitialized); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h @@ -168,6 +168,8 @@ namespace AppInstaller::Logging void SetExecutionStage(uint32_t stage) noexcept; + void SetUseSummary(bool useSummary) noexcept; + std::unique_ptr<TelemetryTraceLogger> CreateSubTraceLogger() const; // Logs the failure info. diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySource.h @@ -17,6 +17,9 @@ namespace AppInstaller::Repository { + // The interval is of 100 nano seconds precision.This is used by file date period and the Windows::Foundation::TimeSpan exposed in COM api. + using TimeSpan = std::chrono::duration<int64_t, std::ratio_multiply<std::ratio<100>, std::nano>>; + struct ISourceReference; struct ISource; @@ -219,6 +222,9 @@ namespace AppInstaller::Repository // Set caller. void SetCaller(std::string caller); + // Set background update check interval. + void SetBackgroundUpdateInterval(TimeSpan interval); + // Execute a search on the source. SearchResult Search(const SearchRequest& request) const; @@ -280,6 +286,7 @@ namespace AppInstaller::Repository std::shared_ptr<ISource> m_source; bool m_isSourceToBeAdded = false; bool m_isComposite = false; + std::optional<TimeSpan> m_backgroundUpdateInterval; mutable PackageTrackingCatalog m_trackingCatalog; }; } diff --git a/src/AppInstallerRepositoryCore/RepositorySource.cpp b/src/AppInstallerRepositoryCore/RepositorySource.cpp @@ -97,25 +97,32 @@ namespace AppInstaller::Repository } // Determines whether (and logs why) a source should be updated before it is opened. - bool ShouldUpdateBeforeOpen(const SourceDetails& details) + bool ShouldUpdateBeforeOpen(const SourceDetails& details, std::optional<TimeSpan> backgroundUpdateInterval) { if (!ContainsAvailablePackagesInternal(details.Origin)) { return false; } - constexpr static auto s_ZeroMins = 0min; - auto autoUpdateTime = User().Get<Setting::AutoUpdateTimeInMinutes>(); + constexpr static TimeSpan s_ZeroMins = 0min; + TimeSpan autoUpdateTime; + if (backgroundUpdateInterval.has_value()) + { + autoUpdateTime = backgroundUpdateInterval.value(); + } + else + { + autoUpdateTime = User().Get<Setting::AutoUpdateTimeInMinutes>(); + } // A value of zero means no auto update, to get update the source run `winget update` if (autoUpdateTime != s_ZeroMins) { - auto autoUpdateTimeMins = std::chrono::minutes(autoUpdateTime); auto timeSinceLastUpdate = std::chrono::system_clock::now() - details.LastUpdateTime; - if (timeSinceLastUpdate > autoUpdateTimeMins) + if (timeSinceLastUpdate > autoUpdateTime) { AICLI_LOG(Repo, Info, << "Source past auto update time [" << - std::chrono::duration_cast<std::chrono::minutes>(autoUpdateTimeMins).count() << " mins]; it has been at least " << + std::chrono::duration_cast<std::chrono::minutes>(autoUpdateTime).count() << " mins]; it has been at least " << std::chrono::duration_cast<std::chrono::minutes>(timeSinceLastUpdate).count() << " mins"); return true; } @@ -278,7 +285,16 @@ namespace AppInstaller::Repository { THROW_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !IsWellKnownSourceEnabled(source)); - SourceDetails details = GetWellKnownSourceDetailsInternal(source); + auto details = GetWellKnownSourceDetailsInternal(source); + + // Populate metadata + SourceList sourceList; + auto sourceDetailsWithMetadata = sourceList.GetSource(details.Name); + if (sourceDetailsWithMetadata) + { + sourceDetailsWithMetadata->CopyMetadataFieldsTo(details); + } + m_sourceReferences.emplace_back(CreateSourceFromDetails(details)); } @@ -454,6 +470,11 @@ namespace AppInstaller::Repository } } + void Source::SetBackgroundUpdateInterval(TimeSpan interval) + { + m_backgroundUpdateInterval = interval; + } + SearchResult Source::Search(const SearchRequest& request) const { THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_source); @@ -539,7 +560,7 @@ namespace AppInstaller::Repository for (auto& sourceReference : m_sourceReferences) { auto& details = sourceReference->GetDetails(); - if (ShouldUpdateBeforeOpen(details)) + if (ShouldUpdateBeforeOpen(details, m_backgroundUpdateInterval)) { try { diff --git a/src/Microsoft.Management.Deployment/Helpers.cpp b/src/Microsoft.Management.Deployment/Helpers.cpp @@ -118,4 +118,54 @@ namespace winrt::Microsoft::Management::Deployment::implementation return {}; } + + std::string GetCallerName() + { + // See if caller name is set by caller + std::string callerName = GetComCallerName(""); + + // Get process string + if (callerName.empty()) + { + try + { + auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); + if (SUCCEEDED(hrGetCallerId)) + { + callerName = AppInstaller::Utility::ConvertToUTF8(TryGetCallerProcessInfo(callerProcessId)); + } + } + CATCH_LOG(); + } + + if (callerName.empty()) + { + callerName = "UnknownComCaller"; + } + + return callerName; + } + + bool IsBackgroundProcessForPolicy() + { + bool isBackgroundProcessForPolicy = false; + try + { + auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); + if (SUCCEEDED(hrGetCallerId) && callerProcessId != GetCurrentProcessId()) + { + // OutOfProc case, we check for explorer.exe + auto callerNameWide = AppInstaller::Utility::ConvertToUTF16(GetCallerName()); + auto processName = AppInstaller::Utility::ConvertToUTF8(std::filesystem::path{ callerNameWide }.filename().wstring()); + if (::AppInstaller::Utility::CaseInsensitiveEquals("explorer.exe", processName) || + ::AppInstaller::Utility::CaseInsensitiveEquals("taskhostw.exe", processName)) + { + isBackgroundProcessForPolicy = true; + } + } + } + CATCH_LOG(); + + return isBackgroundProcessForPolicy; + } } \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/Helpers.h b/src/Microsoft.Management.Deployment/Helpers.h @@ -18,4 +18,6 @@ namespace winrt::Microsoft::Management::Deployment::implementation HRESULT EnsureComCallerHasCapability(Capability requiredCapability); std::pair<HRESULT, DWORD> GetCallerProcessId(); std::wstring TryGetCallerProcessInfo(DWORD callerProcessId); + std::string GetCallerName(); + bool IsBackgroundProcessForPolicy(); } diff --git a/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp b/src/Microsoft.Management.Deployment/PackageCatalogReference.cpp @@ -15,42 +15,28 @@ #include <winget/GroupPolicy.h> #include <AppInstallerErrors.h> #include <AppInstallerStrings.h> +#include <winget/UserSettings.h> #include <Helpers.h> namespace winrt::Microsoft::Management::Deployment::implementation { - namespace + void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::PackageCatalogInfo packageCatalogInfo, ::AppInstaller::Repository::Source sourceReference) { - std::string GetCallerName() + m_info = packageCatalogInfo; + m_sourceReference = std::move(sourceReference); + m_packageCatalogBackgroundUpdateInterval = ::AppInstaller::Settings::User().Get<::AppInstaller::Settings::Setting::AutoUpdateTimeInMinutes>(); + + if (IsBackgroundProcessForPolicy()) { - // See if caller name is set by caller - static auto callerName = GetComCallerName(""); + static constexpr winrt::Windows::Foundation::TimeSpan s_PackageCatalogUpdateIntervalDelay_Base = 168h; //1 week - // Get process string - if (callerName.empty()) - { - try - { - auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); - THROW_IF_FAILED(hrGetCallerId); - callerName = AppInstaller::Utility::ConvertToUTF8(TryGetCallerProcessInfo(callerProcessId)); - } - CATCH_LOG(); - } + // 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); - if (callerName.empty()) - { - callerName = "UnknownComCaller"; - } - - return callerName; + m_packageCatalogBackgroundUpdateInterval = s_PackageCatalogUpdateIntervalDelay_Base + std::chrono::seconds(distribution(randomEngine)); } } - void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::PackageCatalogInfo packageCatalogInfo, ::AppInstaller::Repository::Source sourceReference) - { - m_info = packageCatalogInfo; - m_sourceReference = std::move(sourceReference); - } void PackageCatalogReference::Initialize(winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions options) { m_compositePackageCatalogOptions = options; @@ -89,10 +75,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation return GetConnectCatalogErrorResult(); } - if (!m_acceptSourceAgreements && SourceAgreements().Size() != 0) - { - return GetConnectSourceAgreementsNotAcceptedErrorResult(); - } + std::string callerName = GetCallerName(); ::AppInstaller::ProgressCallback progress; ::AppInstaller::Repository::Source source; @@ -103,9 +86,15 @@ namespace winrt::Microsoft::Management::Deployment::implementation for (uint32_t i = 0; i < m_compositePackageCatalogOptions.Catalogs().Size(); ++i) { auto catalog = m_compositePackageCatalogOptions.Catalogs().GetAt(i); + if (!catalog.AcceptSourceAgreements() && catalog.SourceAgreements().Size() != 0) + { + return GetConnectSourceAgreementsNotAcceptedErrorResult(); + } + winrt::Microsoft::Management::Deployment::implementation::PackageCatalogReference* catalogImpl = get_self<winrt::Microsoft::Management::Deployment::implementation::PackageCatalogReference>(catalog); auto copy = catalogImpl->m_sourceReference; - copy.SetCaller(GetCallerName()); + copy.SetCaller(callerName); + copy.SetBackgroundUpdateInterval(catalog.PackageCatalogBackgroundUpdateInterval()); copy.Open(progress); remoteSources.emplace_back(std::move(copy)); } @@ -140,8 +129,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation } else { + if (!m_acceptSourceAgreements && SourceAgreements().Size() != 0) + { + return GetConnectSourceAgreementsNotAcceptedErrorResult(); + } + source = m_sourceReference; - source.SetCaller(GetCallerName()); + source.SetCaller(callerName); + source.SetBackgroundUpdateInterval(PackageCatalogBackgroundUpdateInterval()); source.Open(progress); } @@ -171,11 +166,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::call_once(m_sourceAgreementsOnceFlag, [&]() { - for (auto const& agreement : m_sourceReference.GetInformation().SourceAgreements) + if (!IsComposite()) { - auto sourceAgreement = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::SourceAgreement>>(); - sourceAgreement->Initialize(agreement); - m_sourceAgreements.Append(*sourceAgreement); + for (auto const& agreement : m_sourceReference.GetInformation().SourceAgreements) + { + auto sourceAgreement = winrt::make_self<wil::details::module_count_wrapper<winrt::Microsoft::Management::Deployment::implementation::SourceAgreement>>(); + sourceAgreement->Initialize(agreement); + m_sourceAgreements.Append(*sourceAgreement); + } } }); return m_sourceAgreements.GetView(); @@ -207,10 +205,28 @@ namespace winrt::Microsoft::Management::Deployment::implementation } void PackageCatalogReference::AcceptSourceAgreements(bool value) { + if (IsComposite()) + { + // Can't set AcceptSourceAgreements on a composite. Callers should set it on each non-composite PackageCatalogReference in the composite. + throw winrt::hresult_illegal_state_change(); + } m_acceptSourceAgreements = value; } bool PackageCatalogReference::AcceptSourceAgreements() { return m_acceptSourceAgreements; } + void PackageCatalogReference::PackageCatalogBackgroundUpdateInterval(winrt::Windows::Foundation::TimeSpan const& value) + { + if (IsComposite()) + { + // Can't set PackageCatalogBackgroundUpdateInterval on a composite. Callers should set it on each non-composite PackageCatalogReference in the composite. + throw winrt::hresult_illegal_state_change(); + } + m_packageCatalogBackgroundUpdateInterval = value; + } + winrt::Windows::Foundation::TimeSpan PackageCatalogReference::PackageCatalogBackgroundUpdateInterval() + { + return m_packageCatalogBackgroundUpdateInterval; + } } diff --git a/src/Microsoft.Management.Deployment/PackageCatalogReference.h b/src/Microsoft.Management.Deployment/PackageCatalogReference.h @@ -25,6 +25,9 @@ namespace winrt::Microsoft::Management::Deployment::implementation // Contract 6.0 bool AcceptSourceAgreements(); void AcceptSourceAgreements(bool value); + // Contract 8.0 + winrt::Windows::Foundation::TimeSpan PackageCatalogBackgroundUpdateInterval(); + void PackageCatalogBackgroundUpdateInterval(winrt::Windows::Foundation::TimeSpan const& value); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: @@ -35,6 +38,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::optional<std::string> m_additionalPackageCatalogArguments; bool m_acceptSourceAgreements = true; 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.cpp b/src/Microsoft.Management.Deployment/PackageManager.cpp @@ -39,6 +39,17 @@ using namespace ::AppInstaller::CLI::Execution; namespace winrt::Microsoft::Management::Deployment::implementation { + PackageManager::PackageManager() + { + auto previousThreadGlobals = m_threadGlobals.SetForCurrentThread(); + // Immediately reset as we only want the thread globals for logging within this object. + previousThreadGlobals.reset(); + // TODO: Disable summary until we log more and have meaningful summary to be sent in the future. + m_threadGlobals.GetTelemetryLogger().SetUseSummary(false); + m_threadGlobals.GetTelemetryLogger().SetCaller(GetCallerName()); + m_threadGlobals.GetTelemetryLogger().LogStartup(true); + } + winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::PackageCatalogReference> PackageManager::GetPackageCatalogs() { Windows::Foundation::Collections::IVector<Microsoft::Management::Deployment::PackageCatalogReference> catalogs{ winrt::single_threaded_vector<Microsoft::Management::Deployment::PackageCatalogReference>() }; diff --git a/src/Microsoft.Management.Deployment/PackageManager.h b/src/Microsoft.Management.Deployment/PackageManager.h @@ -3,6 +3,7 @@ #pragma once #include "PackageManager.g.h" #include "Public/ComClsids.h" +#include <winget/ThreadGlobals.h> #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) // Forward declaration @@ -17,7 +18,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation [uuid(WINGET_OUTOFPROC_COM_CLSID_PackageManager)] struct PackageManager : PackageManagerT<PackageManager> { - PackageManager() = default; + PackageManager(); winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::PackageCatalogReference> GetPackageCatalogs(); winrt::Microsoft::Management::Deployment::PackageCatalogReference GetPredefinedPackageCatalog(winrt::Microsoft::Management::Deployment::PredefinedPackageCatalog const& predefinedPackageCatalog); @@ -41,6 +42,11 @@ namespace winrt::Microsoft::Management::Deployment::implementation DownloadPackageAsync(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::DownloadOptions options); winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Microsoft::Management::Deployment::DownloadResult, winrt::Microsoft::Management::Deployment::PackageDownloadProgress> GetDownloadProgress(winrt::Microsoft::Management::Deployment::CatalogPackage package, winrt::Microsoft::Management::Deployment::PackageCatalogInfo catalogInfo); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; +#endif }; #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) diff --git 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(7)] + [contractversion(8)] apicontract WindowsPackageManagerContract{}; /// State of the install @@ -749,6 +749,12 @@ namespace Microsoft.Management.Deployment Boolean AcceptSourceAgreements; } + + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 8)] + { + /// Time interval for package catalog to check for an update. Setting to zero will disable the check for update. + Windows.Foundation.TimeSpan PackageCatalogBackgroundUpdateInterval; + } } /// Catalogs with PackageCatalogOrigin Predefined diff --git a/src/Microsoft.Management.Deployment/pch.h b/src/Microsoft.Management.Deployment/pch.h @@ -5,4 +5,5 @@ #include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.Collections.h> -#include <mutex>- \ No newline at end of file +#include <mutex> +#include <random>+ \ No newline at end of file