diff --git a/lib/android_build/app/src/androidTest/java/com/microsoft/applications/events/maesdktest/LogManagerDDVUnitTest.java b/lib/android_build/app/src/androidTest/java/com/microsoft/applications/events/maesdktest/LogManagerDDVUnitTest.java
index 5f784a563..e87e17225 100644
--- a/lib/android_build/app/src/androidTest/java/com/microsoft/applications/events/maesdktest/LogManagerDDVUnitTest.java
+++ b/lib/android_build/app/src/androidTest/java/com/microsoft/applications/events/maesdktest/LogManagerDDVUnitTest.java
@@ -578,6 +578,7 @@ public void getDefaultConfig() {
assertThat(tpm, is(notNullValue()));
assertThat(tpm, isA(ILogConfiguration.class));
assertThat(tpm.getLong(LogConfigurationKey.CFG_INT_TPM_MAX_BLOB_BYTES), is(2L * 1024 * 1024));
+ assertThat(tpm.getLong(LogConfigurationKey.CFG_INT_TPM_MAX_EVENTS_PER_UPLOAD), is(1500L));
assertThat(tpm.getLong(LogConfigurationKey.CFG_INT_TPM_MAX_RETRY), is(5L));
assertThat(tpm.getBoolean(LogConfigurationKey.CFG_BOOL_TPM_CLOCK_SKEW_ENABLED), is(true));
ILogConfiguration compat = defaultConfig.getLogConfiguration(LogConfigurationKey.CFG_MAP_COMPAT);
diff --git a/lib/android_build/maesdk/src/main/java/com/microsoft/applications/events/LogConfigurationKey.java b/lib/android_build/maesdk/src/main/java/com/microsoft/applications/events/LogConfigurationKey.java
index 329f17680..0ab7f6ea1 100644
--- a/lib/android_build/maesdk/src/main/java/com/microsoft/applications/events/LogConfigurationKey.java
+++ b/lib/android_build/maesdk/src/main/java/com/microsoft/applications/events/LogConfigurationKey.java
@@ -166,6 +166,8 @@ public enum LogConfigurationKey {
CFG_INT_TPM_MAX_BLOB_BYTES("maxBlobSize", Long.class),
+ CFG_INT_TPM_MAX_EVENTS_PER_UPLOAD("maxEventsPerUpload", Long.class),
+
CFG_BOOL_SESSION_RESET_ENABLED("sessionResetEnabled", Boolean.class);
private String key;
@@ -184,4 +186,3 @@ public Class getValueType() {
return valueType;
}
}
-
diff --git a/lib/config/RuntimeConfig_Default.hpp b/lib/config/RuntimeConfig_Default.hpp
index 504aeefe3..ce0e9e078 100644
--- a/lib/config/RuntimeConfig_Default.hpp
+++ b/lib/config/RuntimeConfig_Default.hpp
@@ -68,6 +68,7 @@ namespace MAT_NS_BEGIN
{CFG_MAP_TPM,
{
{CFG_INT_TPM_MAX_BLOB_BYTES, 2097152},
+ {CFG_INT_TPM_MAX_EVENTS_PER_UPLOAD, 1500},
{CFG_INT_TPM_MAX_RETRY, 5},
{CFG_BOOL_TPM_CLOCK_SKEW_ENABLED, true},
{CFG_STR_TPM_BACKOFF, "E,3000,300000,2,1"},
@@ -233,4 +234,3 @@ namespace MAT_NS_BEGIN
}
MAT_NS_END
-
diff --git a/lib/include/public/ILogConfiguration.hpp b/lib/include/public/ILogConfiguration.hpp
index 1cb8103b8..47ff396e3 100644
--- a/lib/include/public/ILogConfiguration.hpp
+++ b/lib/include/public/ILogConfiguration.hpp
@@ -391,6 +391,11 @@ namespace MAT_NS_BEGIN
///
static constexpr const char* const CFG_INT_TPM_MAX_BLOB_BYTES = "maxBlobSize";
+ ///
+ /// TPM configuration: maximum number of events per upload request. A value of 0 means unlimited.
+ ///
+ static constexpr const char* const CFG_INT_TPM_MAX_EVENTS_PER_UPLOAD = "maxEventsPerUpload";
+
///
/// TPM configuration map
///
@@ -471,4 +476,3 @@ namespace MAT_NS_BEGIN
}
MAT_NS_END
#endif
-
diff --git a/lib/tpm/TransmissionPolicyManager.cpp b/lib/tpm/TransmissionPolicyManager.cpp
index 83b82cf2a..b84dd2410 100644
--- a/lib/tpm/TransmissionPolicyManager.cpp
+++ b/lib/tpm/TransmissionPolicyManager.cpp
@@ -41,6 +41,20 @@ namespace MAT_NS_BEGIN {
return (a > b) ? (a - b) : (b - a);
}
+ unsigned getMaxEventsPerUpload(IRuntimeConfig& config)
+ {
+ const int64_t configuredMaxCount = config[CFG_MAP_TPM][CFG_INT_TPM_MAX_EVENTS_PER_UPLOAD];
+ if (configuredMaxCount <= 0)
+ {
+ return 0;
+ }
+ if (configuredMaxCount > static_cast(std::numeric_limits::max()))
+ {
+ return std::numeric_limits::max();
+ }
+ return static_cast(configuredMaxCount);
+ }
+
MATSDK_LOG_INST_COMPONENT_CLASS(TransmissionPolicyManager, "EventsSDK.TPM", "Events telemetry client - TransmissionPolicyManager class")
TransmissionPolicyManager::TransmissionPolicyManager(ITelemetrySystem& system, ITaskDispatcher& taskDispatcher, IBandwidthController* bandwidthController) :
@@ -218,6 +232,7 @@ namespace MAT_NS_BEGIN {
auto ctx = m_system.createEventsUploadContext();
ctx->requestedMinLatency = m_runningLatency;
+ ctx->requestedMaxCount = getMaxEventsPerUpload(m_config);
addUpload(ctx);
initiateUpload(ctx);
}
@@ -336,6 +351,7 @@ namespace MAT_NS_BEGIN {
if (event->record.latency > EventLatency_RealTime) {
auto ctx = m_system.createEventsUploadContext();
ctx->requestedMinLatency = event->record.latency;
+ ctx->requestedMaxCount = getMaxEventsPerUpload(m_config);
addUpload(ctx);
initiateUpload(ctx);
return;
diff --git a/tests/unittests/TransmissionPolicyManagerTests.cpp b/tests/unittests/TransmissionPolicyManagerTests.cpp
index 6cbdb99f5..36e671012 100644
--- a/tests/unittests/TransmissionPolicyManagerTests.cpp
+++ b/tests/unittests/TransmissionPolicyManagerTests.cpp
@@ -10,11 +10,40 @@
#include "common/MockIBandwidthController.hpp"
#include "tpm/TransmissionPolicyManager.hpp"
#include "TransmitProfiles.hpp"
+#include "offline/MemoryStorage.hpp"
+#include "offline/StorageObserver.hpp"
+#include "packager/Packager.hpp"
+#include "bond/All.hpp"
+#include "bond/generated/CsProtocol_writers.hpp"
+
+#include
using namespace testing;
using namespace MAT;
+class ScopedTpmMaxEventsPerUploadConfig {
+ public:
+ ScopedTpmMaxEventsPerUploadConfig(IRuntimeConfig& config, Variant value)
+ : m_config(config),
+ m_previousMaxCount(config[CFG_MAP_TPM][CFG_INT_TPM_MAX_EVENTS_PER_UPLOAD])
+ {
+ m_config[CFG_MAP_TPM][CFG_INT_TPM_MAX_EVENTS_PER_UPLOAD] = value;
+ }
+
+ ~ScopedTpmMaxEventsPerUploadConfig()
+ {
+ m_config[CFG_MAP_TPM][CFG_INT_TPM_MAX_EVENTS_PER_UPLOAD] = m_previousMaxCount;
+ }
+
+ ScopedTpmMaxEventsPerUploadConfig(const ScopedTpmMaxEventsPerUploadConfig&) = delete;
+ ScopedTpmMaxEventsPerUploadConfig& operator=(const ScopedTpmMaxEventsPerUploadConfig&) = delete;
+
+ private:
+ IRuntimeConfig& m_config;
+ Variant m_previousMaxCount;
+};
+
class TransmissionPolicyManager4Test : public TransmissionPolicyManager {
public:
TransmissionPolicyManager4Test(ITelemetrySystem& system, IBandwidthController* bandwidthController)
@@ -77,6 +106,7 @@ class TransmissionPolicyManagerTests : public StrictMock {
RouteSink initiateUpload{this, &TransmissionPolicyManagerTests::resultInitiateUpload};
RouteSink allUploadsFinished{this, &TransmissionPolicyManagerTests::resultAllUploadsFinished};
+ RouteSink packagedEvents{this, &TransmissionPolicyManagerTests::resultPackagedEvents};
protected:
TransmissionPolicyManagerTests()
@@ -88,6 +118,7 @@ class TransmissionPolicyManagerTests : public StrictMock {
MOCK_METHOD1(resultInitiateUpload, void(EventsUploadContextPtr const &));
MOCK_METHOD0(resultAllUploadsFinished, void());
+ MOCK_METHOD1(resultPackagedEvents, void(EventsUploadContextPtr const &));
virtual void SetUp() override
{
@@ -246,6 +277,27 @@ TEST_F(TransmissionPolicyManagerTests, ImmediateIncomingEventStartsUploadImmedia
ASSERT_THAT(upload, NotNull());
EXPECT_THAT(upload->requestedMinLatency, EventLatency_Max);
+ EXPECT_THAT(upload->requestedMaxCount, 1500u);
+}
+
+TEST_F(TransmissionPolicyManagerTests, ImmediateIncomingEventTreatsNonPositiveMaxEventCountAsUnlimited)
+{
+ auto& config = testing::getSystem().getConfig();
+ ScopedTpmMaxEventsPerUploadConfig maxEventsConfig(config, static_cast(-1));
+ UNREFERENCED_PARAMETER(maxEventsConfig);
+
+ tpm.paused(false);
+
+ auto event = new IncomingEventContext();
+ event->record.latency = EventLatency_Max;
+ EventsUploadContextPtr upload;
+ EXPECT_CALL(*this, resultInitiateUpload(_))
+ .WillOnce(SaveArg<0>(&upload));
+ tpm.eventArrived(event);
+
+ ASSERT_THAT(upload, NotNull());
+ EXPECT_THAT(upload->requestedMinLatency, EventLatency_Max);
+ EXPECT_THAT(upload->requestedMaxCount, 0u);
}
TEST_F(TransmissionPolicyManagerTests, UploadDoesNothingWhenPaused)
@@ -288,13 +340,125 @@ TEST_F(TransmissionPolicyManagerTests, UploadInitiatesUpload)
EventsUploadContextPtr upload;
EXPECT_CALL(*this, resultInitiateUpload(_))
.WillOnce(SaveArg<0>(&upload));
- tpm.uploadAsync(EventLatency_Normal);
+ tpm.uploadAsyncParent(EventLatency_Normal);
EXPECT_THAT(tpm.uploadScheduled(), false);
EXPECT_THAT(upload, NotNull());
+ EXPECT_THAT(upload->requestedMaxCount, 1500u);
EXPECT_THAT(tpm.activeUploads(), Contains(upload));
}
+TEST_F(TransmissionPolicyManagerTests, UploadUsesConfiguredMaxEventCount)
+{
+ auto& config = testing::getSystem().getConfig();
+ ScopedTpmMaxEventsPerUploadConfig maxEventsConfig(config, 3);
+ UNREFERENCED_PARAMETER(maxEventsConfig);
+
+ tpm.uploadScheduled(true);
+ tpm.paused(false);
+
+ EventsUploadContextPtr upload;
+ EXPECT_CALL(*this, resultInitiateUpload(_))
+ .WillOnce(SaveArg<0>(&upload));
+ tpm.uploadAsyncParent(EventLatency_Normal);
+
+ unsigned requestedMaxCount = upload ? upload->requestedMaxCount : 0;
+
+ ASSERT_THAT(upload, NotNull());
+ EXPECT_THAT(requestedMaxCount, 3u);
+}
+
+TEST_F(TransmissionPolicyManagerTests, UploadTreatsNonPositiveMaxEventCountAsUnlimited)
+{
+ auto& config = testing::getSystem().getConfig();
+ ScopedTpmMaxEventsPerUploadConfig maxEventsConfig(config, static_cast(-1));
+ UNREFERENCED_PARAMETER(maxEventsConfig);
+
+ tpm.uploadScheduled(true);
+ tpm.paused(false);
+
+ EventsUploadContextPtr upload;
+ EXPECT_CALL(*this, resultInitiateUpload(_))
+ .WillOnce(SaveArg<0>(&upload));
+ tpm.uploadAsyncParent(EventLatency_Normal);
+
+ ASSERT_THAT(upload, NotNull());
+ EXPECT_THAT(upload->requestedMaxCount, 0u);
+}
+
+TEST_F(TransmissionPolicyManagerTests, UploadCapsOversizedMaxEventCount)
+{
+ auto& config = testing::getSystem().getConfig();
+ int64_t oversizedMaxCount = static_cast(std::numeric_limits::max()) + 1;
+ ScopedTpmMaxEventsPerUploadConfig maxEventsConfig(config, oversizedMaxCount);
+ UNREFERENCED_PARAMETER(maxEventsConfig);
+
+ tpm.uploadScheduled(true);
+ tpm.paused(false);
+
+ EventsUploadContextPtr upload;
+ EXPECT_CALL(*this, resultInitiateUpload(_))
+ .WillOnce(SaveArg<0>(&upload));
+ tpm.uploadAsyncParent(EventLatency_Normal);
+
+ ASSERT_THAT(upload, NotNull());
+ EXPECT_THAT(upload->requestedMaxCount, std::numeric_limits::max());
+}
+
+TEST_F(TransmissionPolicyManagerTests, UploadPackagesNoMoreThanConfiguredMaxEventCount)
+{
+ auto& system = testing::getSystem();
+ auto& config = system.getConfig();
+ ScopedTpmMaxEventsPerUploadConfig maxEventsConfig(config, 3);
+ UNREFERENCED_PARAMETER(maxEventsConfig);
+
+ MemoryStorage storage(system.getLogManager(), config);
+ StorageObserver storageObserver(system, storage);
+ Packager packager(config);
+
+ storageObserver.retrievedEvent >> packager.addEventToPackage;
+ storageObserver.retrievalFinished >> packager.finalizePackage;
+ packager.packagedEvents >> packagedEvents;
+
+ ASSERT_THAT(storageObserver.start(), true);
+ for (unsigned i = 0; i < 10; ++i)
+ {
+ ::CsProtocol::Record sourceRecord;
+ sourceRecord.name = "testEvent" + std::to_string(i);
+ std::vector recordBlob;
+ bond_lite::CompactBinaryProtocolWriter writer(recordBlob);
+ bond_lite::Serialize(writer, sourceRecord);
+
+ StorageRecord record(
+ "r" + std::to_string(i),
+ "tenant-token",
+ EventLatency_Normal,
+ EventPersistence_Normal,
+ 1234567890 + i,
+ std::move(recordBlob));
+ ASSERT_THAT(storage.StoreRecord(record), true);
+ }
+
+ EventsUploadContextPtr packaged;
+ EXPECT_CALL(*this, resultInitiateUpload(_))
+ .WillOnce(Invoke([&storageObserver](EventsUploadContextPtr const& ctx) {
+ storageObserver.retrieveEvents(ctx);
+ }));
+ EXPECT_CALL(*this, resultPackagedEvents(_))
+ .WillOnce(SaveArg<0>(&packaged));
+ tpm.uploadScheduled(true);
+ tpm.paused(false);
+ tpm.uploadAsyncParent(EventLatency_Normal);
+
+ ASSERT_THAT(packaged, NotNull());
+ EXPECT_THAT(packaged->requestedMaxCount, 3u);
+ EXPECT_THAT(packaged->recordIdsAndTenantIds, SizeIs(3));
+ EXPECT_THAT(storage.LastReadRecordCount(), 3u);
+ EXPECT_THAT(storage.GetRecordCount(), 7u);
+ EXPECT_THAT(storage.GetReservedCount(), 3u);
+ EXPECT_THAT(packaged->fromMemory, true);
+}
+
TEST_F(TransmissionPolicyManagerTests, EmptyUploadCeasesUploadingForRunningLatencyNormal)
{
auto upload = tpm.fakeActiveUpload(EventLatency_Normal);
diff --git a/wrappers/obj-c/ODWLogConfiguration.h b/wrappers/obj-c/ODWLogConfiguration.h
index 3cbdaaa61..c176ff96a 100644
--- a/wrappers/obj-c/ODWLogConfiguration.h
+++ b/wrappers/obj-c/ODWLogConfiguration.h
@@ -271,6 +271,11 @@ extern NSString * _Nonnull const ODWCFG_STR_TPM_BACKOFF;
*/
extern NSString * _Nonnull const ODWCFG_INT_TPM_MAX_BLOB_BYTES;
+/*!
+ TPM configuration: maximum events per upload request. Set to 0 for unlimited.
+*/
+extern NSString * _Nonnull const ODWCFG_INT_TPM_MAX_EVENTS_PER_UPLOAD;
+
/*!
TPM configuration map
*/
diff --git a/wrappers/obj-c/ODWLogConfiguration.mm b/wrappers/obj-c/ODWLogConfiguration.mm
index d69ddf70c..ab44d409f 100644
--- a/wrappers/obj-c/ODWLogConfiguration.mm
+++ b/wrappers/obj-c/ODWLogConfiguration.mm
@@ -277,6 +277,11 @@ The minimum time (ms) between storage full notifications.
*/
NSString *const ODWCFG_INT_TPM_MAX_BLOB_BYTES = @"maxBlobSize";
+/*!
+ TPM configuration: maximum events per upload request. Set to 0 for unlimited.
+*/
+NSString *const ODWCFG_INT_TPM_MAX_EVENTS_PER_UPLOAD = @"maxEventsPerUpload";
+
/*!
TPM configuration map
*/