From cbb17d7e359d41d8d7577a9516cf7788c7288442 Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Wed, 11 Mar 2026 18:09:14 +0000 Subject: [PATCH 01/25] chore: Update submodules --- kits/rokt-kit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kits/rokt-kit b/kits/rokt-kit index cfd8174db..b96f92867 160000 --- a/kits/rokt-kit +++ b/kits/rokt-kit @@ -1 +1 @@ -Subproject commit cfd8174db257484693247752bc094835916d4108 +Subproject commit b96f92867ab538673a648b2ecf863598441d66aa From 892a77834cc0e58a076659f18ae40015610ead82 Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Wed, 11 Mar 2026 18:58:47 +0000 Subject: [PATCH 02/25] chore: 5.78.3 (release) ## [5.78.3](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.2...v5.78.3) (2026-03-11) ### Updates & Maintenance * bump actions/upload-artifact from 6 to 7 ([#649](https://github.com/mParticle/mparticle-android-sdk/issues/649)) ([0a188b1](https://github.com/mParticle/mparticle-android-sdk/commit/0a188b1f88efde471f8d6bff2aa8611b1b4bf956)) * Update submodules ([cbb17d7](https://github.com/mParticle/mparticle-android-sdk/commit/cbb17d7e359d41d8d7577a9516cf7788c7288442)) --- CHANGELOG.md | 8 ++++++++ README.md | 6 +++--- build.gradle | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2f5b46a9..028bd5d4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [5.78.3](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.2...v5.78.3) (2026-03-11) + + +### Updates & Maintenance + +* bump actions/upload-artifact from 6 to 7 ([#649](https://github.com/mParticle/mparticle-android-sdk/issues/649)) ([0a188b1](https://github.com/mParticle/mparticle-android-sdk/commit/0a188b1f88efde471f8d6bff2aa8611b1b4bf956)) +* Update submodules ([cbb17d7](https://github.com/mParticle/mparticle-android-sdk/commit/cbb17d7e359d41d8d7577a9516cf7788c7288442)) + ## [5.78.2](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.1...v5.78.2) (2026-02-27) diff --git a/README.md b/README.md index 5ffdcfe48..6304e7651 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can grab the Core SDK via Maven Central. Please see the badge above and foll ```groovy dependencies { - implementation 'com.mparticle:android-core:5.78.2' + implementation 'com.mparticle:android-core:5.78.3' } ``` @@ -31,8 +31,8 @@ Several integrations require additional client-side add-on libraries called "kit ```groovy dependencies { implementation ( - 'com.mparticle:android-example-kit:5.78.2', - 'com.mparticle:android-another-kit:5.78.2' + 'com.mparticle:android-example-kit:5.78.3', + 'com.mparticle:android-another-kit:5.78.3' ) } ``` diff --git a/build.gradle b/build.gradle index ae38d7a0d..8f4ba8e5a 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ subprojects { allprojects { group = 'com.mparticle' - version = '5.78.2-SNAPSHOT' + version = '5.78.3-SNAPSHOT' if (project.hasProperty('isRelease') && project.isRelease) { version = version.toString().replace("-SNAPSHOT", "") } From 08dab75e3715733f6f5655fbf39fbce16a2e01a2 Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Mon, 23 Mar 2026 14:39:14 +0000 Subject: [PATCH 03/25] chore: Update submodules --- kits/appsflyer-kit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kits/appsflyer-kit b/kits/appsflyer-kit index 541195a3c..ff3596bc2 160000 --- a/kits/appsflyer-kit +++ b/kits/appsflyer-kit @@ -1 +1 @@ -Subproject commit 541195a3c0b4a3f3a712486080211564589ac986 +Subproject commit ff3596bc2cc6b3ec85f4ecce29f4f419c8beb005 From 6bfb6af793b8fba6146fc26c5829b18207095ad0 Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Mon, 23 Mar 2026 15:45:38 +0000 Subject: [PATCH 04/25] chore: 5.78.4 (release) ## [5.78.4](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.3...v5.78.4) (2026-03-23) ### Updates & Maintenance * Update submodules ([08dab75](https://github.com/mParticle/mparticle-android-sdk/commit/08dab75e3715733f6f5655fbf39fbce16a2e01a2)) --- CHANGELOG.md | 7 +++++++ README.md | 6 +++--- build.gradle | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 028bd5d4a..eff0c7072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [5.78.4](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.3...v5.78.4) (2026-03-23) + + +### Updates & Maintenance + +* Update submodules ([08dab75](https://github.com/mParticle/mparticle-android-sdk/commit/08dab75e3715733f6f5655fbf39fbce16a2e01a2)) + ## [5.78.3](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.2...v5.78.3) (2026-03-11) diff --git a/README.md b/README.md index 6304e7651..a908cf80e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can grab the Core SDK via Maven Central. Please see the badge above and foll ```groovy dependencies { - implementation 'com.mparticle:android-core:5.78.3' + implementation 'com.mparticle:android-core:5.78.4' } ``` @@ -31,8 +31,8 @@ Several integrations require additional client-side add-on libraries called "kit ```groovy dependencies { implementation ( - 'com.mparticle:android-example-kit:5.78.3', - 'com.mparticle:android-another-kit:5.78.3' + 'com.mparticle:android-example-kit:5.78.4', + 'com.mparticle:android-another-kit:5.78.4' ) } ``` diff --git a/build.gradle b/build.gradle index 8f4ba8e5a..ca31bd288 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ subprojects { allprojects { group = 'com.mparticle' - version = '5.78.3-SNAPSHOT' + version = '5.78.4-SNAPSHOT' if (project.hasProperty('isRelease') && project.isRelease) { version = version.toString().replace("-SNAPSHOT", "") } From 86d8d9748a0c7a796dd0b0be6722f92acef301ea Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Wed, 25 Mar 2026 16:10:11 +0000 Subject: [PATCH 05/25] chore: Update submodules --- kits/appsflyer-kit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kits/appsflyer-kit b/kits/appsflyer-kit index ff3596bc2..e58e6e00b 160000 --- a/kits/appsflyer-kit +++ b/kits/appsflyer-kit @@ -1 +1 @@ -Subproject commit ff3596bc2cc6b3ec85f4ecce29f4f419c8beb005 +Subproject commit e58e6e00baae4ce863e2ef38da46a2746addbdc1 From 83b24bfbffab6eccc235a3fd1f6089f57462e446 Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Wed, 25 Mar 2026 16:26:15 +0000 Subject: [PATCH 06/25] chore: 5.78.5 (release) ## [5.78.5](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.4...v5.78.5) (2026-03-25) ### Updates & Maintenance * Update submodules ([86d8d97](https://github.com/mParticle/mparticle-android-sdk/commit/86d8d9748a0c7a796dd0b0be6722f92acef301ea)) --- CHANGELOG.md | 7 +++++++ README.md | 6 +++--- build.gradle | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eff0c7072..bd0d2cbce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [5.78.5](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.4...v5.78.5) (2026-03-25) + + +### Updates & Maintenance + +* Update submodules ([86d8d97](https://github.com/mParticle/mparticle-android-sdk/commit/86d8d9748a0c7a796dd0b0be6722f92acef301ea)) + ## [5.78.4](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.3...v5.78.4) (2026-03-23) diff --git a/README.md b/README.md index a908cf80e..a0a16d657 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can grab the Core SDK via Maven Central. Please see the badge above and foll ```groovy dependencies { - implementation 'com.mparticle:android-core:5.78.4' + implementation 'com.mparticle:android-core:5.78.5' } ``` @@ -31,8 +31,8 @@ Several integrations require additional client-side add-on libraries called "kit ```groovy dependencies { implementation ( - 'com.mparticle:android-example-kit:5.78.4', - 'com.mparticle:android-another-kit:5.78.4' + 'com.mparticle:android-example-kit:5.78.5', + 'com.mparticle:android-another-kit:5.78.5' ) } ``` diff --git a/build.gradle b/build.gradle index ca31bd288..74315ca09 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ subprojects { allprojects { group = 'com.mparticle' - version = '5.78.4-SNAPSHOT' + version = '5.78.5-SNAPSHOT' if (project.hasProperty('isRelease') && project.isRelease) { version = version.toString().replace("-SNAPSHOT", "") } From 20f723f27cf296c879ceebc9d234edca039ab371 Mon Sep 17 00:00:00 2001 From: Robert Ing Date: Wed, 8 Apr 2026 11:41:09 -0400 Subject: [PATCH 07/25] fix: increase MPLatch timeout from 5s to 30s (#695) * fix: increase MPLatch timeout from 5s to 30s The 5-second timeout is too short for WebView JS bridge tests running on newer GitHub Actions runner images (ubuntu24/20260309.50+). The MParticleJSInterfaceITest tests consistently fail because JS execution in the emulator WebView doesn't complete within 5 seconds on these environments. Increasing to 30 seconds provides sufficient margin while still catching genuine hangs via the workflow-level timeout-minutes: 15. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: use explicit 30s timeout in JS bridge tests only Instead of increasing the global MPLatch timeout (used by 20+ test files), use an explicit 30-second await in MParticleJSInterfaceITest only. WebView JS execution needs more time on newer CI runner images, but other tests don't need the longer timeout. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../internal/MParticleJSInterfaceITest.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/android-core/src/androidTest/java/com/mparticle/internal/MParticleJSInterfaceITest.java b/android-core/src/androidTest/java/com/mparticle/internal/MParticleJSInterfaceITest.java index 0a1832b0a..55d40222b 100644 --- a/android-core/src/androidTest/java/com/mparticle/internal/MParticleJSInterfaceITest.java +++ b/android-core/src/androidTest/java/com/mparticle/internal/MParticleJSInterfaceITest.java @@ -33,6 +33,8 @@ import com.mparticle.testutils.BaseCleanStartedEachTest; import com.mparticle.testutils.BuildConfig; import com.mparticle.testutils.MPLatch; + +import java.util.concurrent.TimeUnit; import com.mparticle.testutils.RandomUtils; import org.json.JSONArray; @@ -68,6 +70,7 @@ public class MParticleJSInterfaceITest extends BaseCleanStartedEachTest { private static boolean sdkFetchedSuccessfully = false; private static String bridgeToken = new RandomUtils().getAlphaString(5); private static String bridgeVersion = "2"; + private static final int WEBVIEW_TIMEOUT_SECONDS = 30; private static final String jsStartupMParticle = "window.mParticle = {\n" + " config: {\n" + @@ -238,7 +241,7 @@ public void setUserAttribute(String json) { } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -263,7 +266,7 @@ public void removeUserAttribute(String json) { } } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -304,7 +307,7 @@ public void setUserAttribute(String json) { } } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -361,7 +364,7 @@ public void logEvent(String json) { } } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -443,7 +446,7 @@ public void logEvent(String json) { } }); assertNull(error.value); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -470,7 +473,7 @@ public void logout(String json) { } } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -496,7 +499,7 @@ public void logout() { latch.countDown(); } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -522,7 +525,7 @@ public void login(String json) { } } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -548,7 +551,7 @@ public void login() { latch.countDown(); } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } @@ -574,7 +577,7 @@ public void modify(String json) { } } }); - latch.await(); + latch.await(WEBVIEW_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertTrue(called.value); } From ca88322ac7ef8649ed111cf28afb81d99cdb271a Mon Sep 17 00:00:00 2001 From: junias-rokt Date: Thu, 23 Apr 2026 13:18:47 -0400 Subject: [PATCH 08/25] feat: Add max persistence age override option [TRIAGE-608] (#699) * TRIAGE-608: Add max persistence age override option * TRIAGE-608: Update throttl ts on success + tests * TRIAGE-608: Make properties @VisibleForTesting * TRIAGE-608: Fix retry-on-failure throttle logic --- .../com.mparticle/MParticleOptionsTest.kt | 40 +++++++ .../database/services/MessageServiceTest.kt | 66 +++++++++++ .../database/services/SessionServiceTest.kt | 54 +++++++++ .../database/services/UploadServiceTest.kt | 52 +++++++++ .../java/com/mparticle/MParticleOptions.java | 51 +++++++++ .../com/mparticle/internal/ConfigManager.java | 13 +++ .../com/mparticle/internal/UploadHandler.java | 51 +++++++++ .../database/services/MParticleDBManager.java | 32 ++++++ .../database/services/MessageService.java | 16 +++ .../database/services/SessionService.java | 17 +++ .../database/services/UploadService.java | 16 +++ .../mparticle/internal/UploadHandlerTest.kt | 107 ++++++++++++++++++ 12 files changed, 515 insertions(+) create mode 100644 android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/UploadServiceTest.kt diff --git a/android-core/src/androidTest/kotlin/com.mparticle/MParticleOptionsTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/MParticleOptionsTest.kt index 826b3139b..6249078c0 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/MParticleOptionsTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/MParticleOptionsTest.kt @@ -694,6 +694,46 @@ class MParticleOptionsTest : BaseAbstractTest() { Assert.assertNull(options.configMaxAge) } + @Test + fun testPersistenceMaxAgeSeconds() { + // nothing set, should return null (SDK will fall back to the 90-day default) + var options = + MParticleOptions + .builder(mContext) + .credentials("key", "secret") + .build() + Assert.assertNull(options.persistenceMaxAgeSeconds) + + // positive number should be preserved + val testValue = Math.abs(ran.nextInt()) + 1 + options = + MParticleOptions + .builder(mContext) + .credentials("key", "secret") + .persistenceMaxAgeSeconds(testValue) + .build() + Assert.assertEquals(testValue, options.persistenceMaxAgeSeconds) + + // zero is non-positive and should be rejected (differs from configMaxAgeSeconds which + // accepts zero as "always stale") - mirrors iOS SDK behaviour + options = + MParticleOptions + .builder(mContext) + .credentials("key", "secret") + .persistenceMaxAgeSeconds(0) + .build() + Assert.assertNull(options.persistenceMaxAgeSeconds) + + // negative numbers should be rejected + options = + MParticleOptions + .builder(mContext) + .credentials("key", "secret") + .persistenceMaxAgeSeconds(-5) + .build() + Assert.assertNull(options.persistenceMaxAgeSeconds) + } + @Test fun testAndroidIdLogMessage() { val infoLogs = ArrayList() diff --git a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/MessageServiceTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/MessageServiceTest.kt index 9b1db3a01..290e4b95d 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/MessageServiceTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/MessageServiceTest.kt @@ -400,6 +400,72 @@ class MessageServiceTest : BaseMPServiceTest() { Assert.assertEquals(MessageService.getMessagesForUpload(database).size.toLong(), 20) } + @Test + @Throws(JSONException::class) + fun testDeleteMessagesOlderThan() { + val sessionId = UUID.randomUUID().toString() + val now = System.currentTimeMillis() + val oneDayMillis = 24L * 60L * 60L * 1000L + // Insert 5 "old" messages dated 10 days ago and 5 "recent" messages dated 1 hour ago. + for (i in 0 until 5) { + val oldMessage = + BaseMPMessage + .Builder("custom_event") + .timestamp(now - 10L * oneDayMillis) + .build( + InternalSession().apply { mSessionID = sessionId }, + null, + 1L, + ) + MessageService.insertMessage(database, "apiKey", oldMessage, 1L, null, null) + } + for (i in 0 until 5) { + val recentMessage = + BaseMPMessage + .Builder("custom_event") + .timestamp(now - 60L * 60L * 1000L) + .build( + InternalSession().apply { mSessionID = sessionId }, + null, + 1L, + ) + MessageService.insertMessage(database, "apiKey", recentMessage, 1L, null, null) + } + Assert.assertEquals( + 10L, + MessageService.getMessagesForUpload(database).size.toLong(), + ) + + // Cut off at 7 days ago - the 5 old messages should be removed and the 5 recent kept. + val cutoffMillis = now - 7L * oneDayMillis + val deleted = MessageService.deleteMessagesOlderThan(database, cutoffMillis) + Assert.assertEquals(5, deleted.toLong()) + Assert.assertEquals( + 5L, + MessageService.getMessagesForUpload(database).size.toLong(), + ) + + // Rows exactly at the cutoff must not be removed (strict `<` predicate). + val exactlyAtCutoffMessage = + BaseMPMessage + .Builder("custom_event") + .timestamp(cutoffMillis) + .build( + InternalSession().apply { mSessionID = sessionId }, + null, + 1L, + ) + MessageService.insertMessage(database, "apiKey", exactlyAtCutoffMessage, 1L, null, null) + Assert.assertEquals( + 0, + MessageService.deleteMessagesOlderThan(database, cutoffMillis).toLong(), + ) + Assert.assertEquals( + 6L, + MessageService.getMessagesForUpload(database).size.toLong(), + ) + } + private fun getMaxId(messages: List): Int { var max = 0 for (message in messages) { diff --git a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/SessionServiceTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/SessionServiceTest.kt index 80ab0de18..eeb790001 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/SessionServiceTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/SessionServiceTest.kt @@ -4,6 +4,7 @@ import android.database.Cursor import com.mparticle.internal.BatchId import com.mparticle.internal.MessageBatch import com.mparticle.internal.database.tables.SessionTable +import org.json.JSONException import org.json.JSONObject import org.junit.Assert import org.junit.Assert.assertEquals @@ -76,6 +77,59 @@ class SessionServiceTest : BaseMPServiceTest() { } } + @Test + @Throws(JSONException::class) + fun testDeleteSessionsOlderThan() { + val now = System.currentTimeMillis() + val oneDayMillis = 24L * 60L * 60L * 1000L + val oldEndTime = now - 10L * oneDayMillis + val recentEndTime = now - 60L * 60L * 1000L + + // Insert 5 sessions whose END_TIME is 10 days ago and 5 whose END_TIME is 1 hour ago. + // insertSession seeds END_TIME = START_TIME, so we call updateSessionEndTime to model + // the production flow where subsequent events advance END_TIME independently. + for (i in 0 until 5) { + val oldSessionId = UUID.randomUUID().toString() + SessionService.insertSession(database, getMpMessage(oldSessionId), "apiKey", "{}", "{}", 1L) + SessionService.updateSessionEndTime(database, oldSessionId, oldEndTime, 0) + } + for (i in 0 until 5) { + val recentSessionId = UUID.randomUUID().toString() + SessionService.insertSession(database, getMpMessage(recentSessionId), "apiKey", "{}", "{}", 1L) + SessionService.updateSessionEndTime(database, recentSessionId, recentEndTime, 0) + } + assertEquals(10, countSessions()) + + // Cut off at 7 days ago - the 5 old sessions should be removed and the 5 recent kept. + val cutoffMillis = now - 7L * oneDayMillis + val deleted = SessionService.deleteSessionsOlderThan(database, cutoffMillis) + assertEquals(5, deleted) + assertEquals(5, countSessions()) + + // Rows whose END_TIME is exactly at the cutoff must not be removed (strict `<` predicate). + val boundarySessionId = UUID.randomUUID().toString() + SessionService.insertSession(database, getMpMessage(boundarySessionId), "apiKey", "{}", "{}", 1L) + SessionService.updateSessionEndTime(database, boundarySessionId, cutoffMillis, 0) + assertEquals(0, SessionService.deleteSessionsOlderThan(database, cutoffMillis)) + assertEquals(6, countSessions()) + } + + private fun countSessions(): Int { + var count = 0 + var cursor: Cursor? = null + try { + cursor = SessionService.getSessions(database) + while (cursor.moveToNext()) { + count++ + } + } finally { + if (cursor != null && !cursor.isClosed) { + cursor.close() + } + } + return count + } + internal inner class MockMessageBatch( var id: Int, ) : MessageBatch() { diff --git a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/UploadServiceTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/UploadServiceTest.kt new file mode 100644 index 000000000..5b2ed9895 --- /dev/null +++ b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/UploadServiceTest.kt @@ -0,0 +1,52 @@ +package com.mparticle.internal.database.services + +import com.mparticle.internal.Constants +import com.mparticle.internal.database.UploadSettings +import com.mparticle.networking.NetworkOptions +import org.json.JSONException +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Test + +class UploadServiceTest : BaseMPServiceTest() { + + @Test + @Throws(JSONException::class) + fun testDeleteUploadsOlderThan() { + val now = System.currentTimeMillis() + val oneDayMillis = 24L * 60L * 60L * 1000L + val uploadSettings = UploadSettings( + "apiKey", + "secret", + NetworkOptions.builder().build(), + "", + "", + ) + + // Insert 5 uploads dated 10 days ago and 5 uploads dated 1 hour ago. + // insertUpload reads CREATED_AT from the message's TIMESTAMP ("ct") key. + for (i in 0 until 5) { + UploadService.insertUpload(database, uploadJson(now - 10L * oneDayMillis), uploadSettings) + } + for (i in 0 until 5) { + UploadService.insertUpload(database, uploadJson(now - 60L * 60L * 1000L), uploadSettings) + } + assertEquals(10, UploadService.getReadyUploads(database).size) + + // Cut off at 7 days ago - the 5 old uploads should be removed and the 5 recent kept. + val cutoffMillis = now - 7L * oneDayMillis + val deleted = UploadService.deleteUploadsOlderThan(database, cutoffMillis) + assertEquals(5, deleted) + assertEquals(5, UploadService.getReadyUploads(database).size) + + // Rows whose CREATED_AT is exactly at the cutoff must not be removed (strict `<` predicate). + UploadService.insertUpload(database, uploadJson(cutoffMillis), uploadSettings) + assertEquals(0, UploadService.deleteUploadsOlderThan(database, cutoffMillis)) + assertEquals(6, UploadService.getReadyUploads(database).size) + } + + @Throws(JSONException::class) + private fun uploadJson(timestampMillis: Long): JSONObject = JSONObject() + .put(Constants.MessageKey.TIMESTAMP, timestampMillis) + .put("payload", "test") +} diff --git a/android-core/src/main/java/com/mparticle/MParticleOptions.java b/android-core/src/main/java/com/mparticle/MParticleOptions.java index e6fd4c123..3b4002224 100644 --- a/android-core/src/main/java/com/mparticle/MParticleOptions.java +++ b/android-core/src/main/java/com/mparticle/MParticleOptions.java @@ -42,6 +42,7 @@ public class MParticleOptions { private Integer mUploadInterval = ConfigManager.DEFAULT_UPLOAD_INTERVAL; //seconds private Integer mSessionTimeout = ConfigManager.DEFAULT_SESSION_TIMEOUT_SECONDS; //seconds private Integer mConfigMaxAge = null; + private Integer mPersistenceMaxAgeSeconds = null; private Boolean mUnCaughtExceptionLogging = false; private MParticle.LogLevel mLogLevel = MParticle.LogLevel.DEBUG; private AttributionListener mAttributionListener; @@ -118,6 +119,13 @@ public MParticleOptions(@NonNull Builder builder) { this.mConfigMaxAge = builder.configMaxAge; } } + if (builder.persistenceMaxAgeSeconds != null) { + if (builder.persistenceMaxAgeSeconds <= 0) { + Logger.warning("Persistence Max Age must be a positive number, disregarding value."); + } else { + this.mPersistenceMaxAgeSeconds = builder.persistenceMaxAgeSeconds; + } + } if (builder.unCaughtExceptionLogging != null) { this.mUnCaughtExceptionLogging = builder.unCaughtExceptionLogging; } @@ -290,6 +298,22 @@ public Integer getConfigMaxAge() { return mConfigMaxAge; } + /** + * The maximum threshold (in seconds) for locally persisted events, batches, and sessions. + *

+ * When {@code null} (the default), records are retained for 90 days before being deleted. + * Values less than or equal to zero are rejected at build time and result in the default + * being used. + * + * @return the configured maximum persistence age in seconds, or {@code null} when the default + * (90 days) applies + * @see Builder#persistenceMaxAgeSeconds(int) + */ + @Nullable + public Integer getPersistenceMaxAgeSeconds() { + return mPersistenceMaxAgeSeconds; + } + @NonNull public Boolean isUncaughtExceptionLoggingEnabled() { return mUnCaughtExceptionLogging; @@ -403,6 +427,7 @@ public static class Builder { private Integer uploadInterval = null; private Integer sessionTimeout = null; private Integer configMaxAge = null; + private Integer persistenceMaxAgeSeconds = null; private Boolean unCaughtExceptionLogging = null; MParticle.LogLevel logLevel = null; BaseIdentityTask identityTask; @@ -622,6 +647,32 @@ public Builder configMaxAgeSeconds(int configMaxAge) { return this; } + /** + * Set a maximum threshold for locally persisted events, batches, and sessions, in seconds. + *

+ * By default, data is persisted for 90 days before being deleted to minimize data loss; + * however, this can lead to excessive storage usage on some users' devices. This is + * exacerbated if your app logs a large number of events, or events carrying a lot of data + * (attributes, etc.). + *

+ * Set a lower value (for example, 48 hours or 1 week) if you have storage usage concerns. + * Alternatively, if you have data loss concerns, set a longer value than the default. + *

+ * This is the Android equivalent of the iOS SDK's + * {@code MParticleOptions.persistenceMaxAgeSeconds} option. + * + * @param persistenceMaxAgeSeconds the upper limit, in seconds, for how long persisted + * data may live on disk. Must be greater than zero; + * non-positive values are rejected and the default + * (90 days) is used instead + * @return the instance of the builder, for chaining calls + */ + @NonNull + public Builder persistenceMaxAgeSeconds(int persistenceMaxAgeSeconds) { + this.persistenceMaxAgeSeconds = persistenceMaxAgeSeconds; + return this; + } + /** * Enable or disable mParticle exception handling to automatically log events on uncaught exceptions. * diff --git a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java index 8b7d61569..105dd8d54 100644 --- a/android-core/src/main/java/com/mparticle/internal/ConfigManager.java +++ b/android-core/src/main/java/com/mparticle/internal/ConfigManager.java @@ -106,6 +106,8 @@ public class ConfigManager { private String mDataplanId; private Integer mDataplanVersion; private Integer mMaxConfigAge; + @Nullable + private Integer mPersistenceMaxAgeSeconds; public static final int DEFAULT_CONNECTION_TIMEOUT_SECONDS = 30; public static final int MINIMUM_CONNECTION_TIMEOUT_SECONDS = 1; public static final int DEFAULT_SESSION_TIMEOUT_SECONDS = 60; @@ -136,6 +138,17 @@ public ConfigManager(Context context) { public ConfigManager(@NonNull MParticleOptions options) { this(options.getContext(), options.getEnvironment(), options.getApiKey(), options.getApiSecret(), options.getDataplanOptions(), options.getDataplanId(), options.getDataplanVersion(), options.getConfigMaxAge(), options.getConfigurationsForTarget(ConfigManager.class), options.getSideloadedKits()); + mPersistenceMaxAgeSeconds = options.getPersistenceMaxAgeSeconds(); + } + + /** + * @return the configured maximum persistence age in seconds, or {@code null} when the SDK + * should fall back to its 90-day default. + * @see MParticleOptions.Builder#persistenceMaxAgeSeconds(int) + */ + @Nullable + public Integer getPersistenceMaxAgeSeconds() { + return mPersistenceMaxAgeSeconds; } public ConfigManager(@NonNull Context context, @Nullable MParticle.Environment environment, @Nullable String apiKey, @Nullable String apiSecret, @Nullable MParticleOptions.DataplanOptions dataplanOptions, @Nullable String dataplanId, @Nullable Integer dataplanVersion, @Nullable Integer configMaxAge, @Nullable List> configurations, @Nullable List sideloadedKits) { diff --git a/android-core/src/main/java/com/mparticle/internal/UploadHandler.java b/android-core/src/main/java/com/mparticle/internal/UploadHandler.java index 38e68d7db..2ce074c28 100644 --- a/android-core/src/main/java/com/mparticle/internal/UploadHandler.java +++ b/android-core/src/main/java/com/mparticle/internal/UploadHandler.java @@ -8,6 +8,7 @@ import android.os.Message; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.mparticle.MParticle; import com.mparticle.audience.AudienceResponse; @@ -56,6 +57,27 @@ public class UploadHandler extends BaseHandler { */ public static final int INIT_CONFIG = 6; + /** + * Default retention window for persisted events, batches, and sessions when + * {@link com.mparticle.MParticleOptions.Builder#persistenceMaxAgeSeconds(int)} is not set. + * Matches the iOS SDK's 90-day default. + */ + @VisibleForTesting + static final long DEFAULT_PERSISTENCE_MAX_AGE_MILLIS = 90L * 24L * 60L * 60L * 1000L; + + /** + * Minimum interval between age-based persistence sweeps, matching the iOS SDK's 24 hour + * throttle on {@code cleanUp}. + */ + @VisibleForTesting + static final long PERSISTENCE_CLEANUP_INTERVAL_MILLIS = 24L * 60L * 60L * 1000L; + + /** + * Unix-epoch millisecond timestamp of the last successful age-based sweep. Zero means + * "never run in this process". + */ + private long mLastPersistenceCleanupMillis = 0L; + private final SharedPreferences mPreferences; private final SegmentDatabase audienceDB; @@ -186,6 +208,7 @@ public void prepareMessageUploads(UploadSettings uploadSettings) throws Exceptio * This method is responsible for looking for batches that are ready to be uploaded, and uploading them. */ protected void upload() { + maybePrunePersistedRecords(System.currentTimeMillis()); mParticleDBManager.cleanupUploadMessages(); try { List readyUploads = mParticleDBManager.getReadyUploads(); @@ -211,6 +234,34 @@ protected void upload() { } } + /** + * Run an age-based retention sweep across persisted events, batches, and sessions at most + * once every {@link #PERSISTENCE_CLEANUP_INTERVAL_MILLIS}. When the consumer has not + * configured {@link com.mparticle.MParticleOptions.Builder#persistenceMaxAgeSeconds(int)}, + * the default 90-day window is used. The throttle timestamp is only advanced on a + * successful sweep so that transient failures (for example a locked database) can be + * retried on the next upload cycle rather than deferred for 24 hours. + * + * @param nowMillis current time in unix-epoch milliseconds + */ + @VisibleForTesting + void maybePrunePersistedRecords(long nowMillis) { + if (nowMillis - mLastPersistenceCleanupMillis < PERSISTENCE_CLEANUP_INTERVAL_MILLIS) { + return; + } + if (mParticleDBManager == null) { + return; + } + Integer configured = mConfigManager == null ? null : mConfigManager.getPersistenceMaxAgeSeconds(); + long maxAgeMillis = (configured == null) + ? DEFAULT_PERSISTENCE_MAX_AGE_MILLIS + : configured.longValue() * 1000L; + long cutoffMillis = nowMillis - maxAgeMillis; + if (mParticleDBManager.deleteRecordsOlderThan(cutoffMillis)) { + mLastPersistenceCleanupMillis = nowMillis; + } + } + void uploadMessage(int id, String message, UploadSettings uploadSettings) throws IOException, MParticleApiClientImpl.MPThrottleException { int responseCode = -1; boolean sampling = false; diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java b/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java index 9e9015946..9f46908d9 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/MParticleDBManager.java @@ -7,6 +7,7 @@ import android.os.Handler; import android.os.Looper; +import androidx.annotation.CheckResult; import androidx.annotation.Nullable; import com.mparticle.MParticle; @@ -206,6 +207,37 @@ public void deleteMessagesAndSessions(String currentSessionId) { } } + /** + * Age-based retention sweep across the three persistence tables. + *

+ * Deletes any messages and uploads whose {@code CREATED_AT} is strictly less than + * {@code cutoffMillis}, and any sessions whose {@code END_TIME} is strictly less than + * {@code cutoffMillis}. + * {@code MPPersistenceController.deleteRecordsOlderThan:}, but reports success so + * callers can decide whether to arm retry/throttle state. + * + * @param cutoffMillis the unix-epoch millisecond cutoff; rows older than this are removed + * @return {@code true} if the transaction committed successfully, {@code false} if any + * exception was caught (and logged) during the sweep + */ + @CheckResult + public boolean deleteRecordsOlderThan(long cutoffMillis) { + MPDatabase db = getDatabase(); + try { + db.beginTransaction(); + MessageService.deleteMessagesOlderThan(db, cutoffMillis); + UploadService.deleteUploadsOlderThan(db, cutoffMillis); + SessionService.deleteSessionsOlderThan(db, cutoffMillis); + db.setTransactionSuccessful(); + return true; + } catch (Exception e) { + Logger.warning(e, "Error pruning persisted records older than " + cutoffMillis + " ms."); + return false; + } finally { + db.endTransaction(); + } + } + private HashMap getUploadMessageByBatchIdMap(List readyMessages, MPDatabase db, ConfigManager configManager) throws JSONException { return getUploadMessageByBatchIdMap(readyMessages, db, configManager, false); } diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/MessageService.java b/android-core/src/main/java/com/mparticle/internal/database/services/MessageService.java index 135352ef8..38814cf34 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/MessageService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/MessageService.java @@ -105,6 +105,22 @@ public static int deleteOldMessages(MPDatabase database, String currentSessionId selectionArgs); } + /** + * Delete messages whose {@link MessageTableColumns#CREATED_AT} is strictly less than + * {@code cutoffMillis}. + * + * @param database the message database + * @param cutoffMillis the unix-epoch millisecond cutoff; rows older than this are removed + * @return the number of rows deleted + */ + public static int deleteMessagesOlderThan(MPDatabase database, long cutoffMillis) { + String[] whereArgs = new String[]{Long.toString(cutoffMillis)}; + return database.delete( + MessageTableColumns.TABLE_NAME, + MessageTableColumns.CREATED_AT + " < ?", + whereArgs); + } + public static boolean hasMessagesForUpload(MPDatabase database) { Cursor messageIds = null; try { diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/SessionService.java b/android-core/src/main/java/com/mparticle/internal/database/services/SessionService.java index 142cf11fb..7b33fd6fd 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/SessionService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/SessionService.java @@ -33,6 +33,23 @@ public static int deleteSessions(MPDatabase database, String currentSessionId) { return database.delete(TABLE_NAME, SessionTableColumns.SESSION_ID + "!=? ", selectionArgs); } + /** + * Delete sessions whose {@link SessionTableColumns#END_TIME} is strictly less than + * {@code cutoffMillis}. + * + * @param database the session database + * @param cutoffMillis the unix-epoch millisecond cutoff; sessions that ended before this are + * removed + * @return the number of rows deleted + */ + public static int deleteSessionsOlderThan(MPDatabase database, long cutoffMillis) { + String[] whereArgs = new String[]{Long.toString(cutoffMillis)}; + return database.delete( + TABLE_NAME, + SessionTableColumns.END_TIME + " < ?", + whereArgs); + } + /** * delete Session entries with session_id that are not a part of the Set * diff --git a/android-core/src/main/java/com/mparticle/internal/database/services/UploadService.java b/android-core/src/main/java/com/mparticle/internal/database/services/UploadService.java index 912c40aeb..90e4809d3 100644 --- a/android-core/src/main/java/com/mparticle/internal/database/services/UploadService.java +++ b/android-core/src/main/java/com/mparticle/internal/database/services/UploadService.java @@ -22,6 +22,22 @@ public static int cleanupUploadMessages(MPDatabase database) { return database.delete(UploadTableColumns.TABLE_NAME, "length(" + UploadTableColumns.MESSAGE + ") > " + Constants.LIMIT_MAX_UPLOAD_SIZE, null); } + /** + * Delete uploads whose {@link UploadTableColumns#CREATED_AT} is strictly less than + * {@code cutoffMillis}. + * + * @param database the upload database + * @param cutoffMillis the unix-epoch millisecond cutoff; rows older than this are removed + * @return the number of rows deleted + */ + public static int deleteUploadsOlderThan(MPDatabase database, long cutoffMillis) { + String[] whereArgs = new String[]{Long.toString(cutoffMillis)}; + return database.delete( + UploadTableColumns.TABLE_NAME, + UploadTableColumns.CREATED_AT + " < ?", + whereArgs); + } + /** * Generic method to insert a new upload, * either a regular message batch, or a session history. diff --git a/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt index ff4640404..3c64cadb8 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/UploadHandlerTest.kt @@ -130,6 +130,113 @@ class UploadHandlerTest { handler.uploadMessage(522, "", mConfigManager.uploadSettings) } + @Test + fun testMaybePrunePersistedRecordsUsesDefaultMaxAgeWhenUnconfigured() { + val db = Mockito.mock(MParticleDBManager::class.java) + val config = Mockito.mock(ConfigManager::class.java) + // Mockito's default Answer returns Integer.valueOf(0) for Integer wrapper return + // types, so stub null explicitly to model the "consumer did not configure a value" + // path. The production code must then fall back to DEFAULT_PERSISTENCE_MAX_AGE_MILLIS. + Mockito.doReturn(null).`when`(config).persistenceMaxAgeSeconds + val uploadHandler = + UploadHandler( + MockContext(), + config, + Mockito.mock(AppStateManager::class.java), + Mockito.mock(MessageManager::class.java), + db, + Mockito.mock(KitFrameworkWrapper::class.java), + ) + val capturedCutoff = java.util.concurrent.atomic.AtomicLong(Long.MIN_VALUE) + Mockito.doAnswer { invocation -> + capturedCutoff.set(invocation.getArgument(0)) + true + }.`when`(db).deleteRecordsOlderThan(Mockito.anyLong()) + + val now = 1_700_000_000_000L + uploadHandler.maybePrunePersistedRecords(now) + + Mockito.verify(db, Mockito.times(1)).deleteRecordsOlderThan(Mockito.anyLong()) + Assert.assertEquals( + now - UploadHandler.DEFAULT_PERSISTENCE_MAX_AGE_MILLIS, + capturedCutoff.get(), + ) + } + + @Test + fun testMaybePrunePersistedRecordsUsesConfiguredMaxAge() { + val db = Mockito.mock(MParticleDBManager::class.java) + val config = Mockito.mock(ConfigManager::class.java) + // 1 hour retention window -> cutoff should be exactly now - 3_600_000 ms. + // Guards the seconds-to-millis conversion in maybePrunePersistedRecords. + Mockito.`when`(config.persistenceMaxAgeSeconds).thenReturn(3600) + val uploadHandler = + UploadHandler( + MockContext(), + config, + Mockito.mock(AppStateManager::class.java), + Mockito.mock(MessageManager::class.java), + db, + Mockito.mock(KitFrameworkWrapper::class.java), + ) + val capturedCutoff = java.util.concurrent.atomic.AtomicLong(Long.MIN_VALUE) + Mockito.doAnswer { invocation -> + capturedCutoff.set(invocation.getArgument(0)) + true + }.`when`(db).deleteRecordsOlderThan(Mockito.anyLong()) + + val now = 1_700_000_000_000L + uploadHandler.maybePrunePersistedRecords(now) + + Assert.assertEquals(now - 3_600_000L, capturedCutoff.get()) + } + + @Test + fun testMaybePrunePersistedRecordsHonorsTwentyFourHourThrottle() { + val db = handler.mParticleDBManager + // The sweep must succeed so the throttle arms; otherwise every call retries. + Mockito.`when`(db.deleteRecordsOlderThan(Mockito.anyLong())).thenReturn(true) + val t0 = 1_000_000_000_000L + + // First call always runs the sweep. + handler.maybePrunePersistedRecords(t0) + Mockito.verify(db, Mockito.times(1)).deleteRecordsOlderThan(Mockito.anyLong()) + + // One millisecond before the throttle interval expires - still a no-op. + handler.maybePrunePersistedRecords(t0 + UploadHandler.PERSISTENCE_CLEANUP_INTERVAL_MILLIS - 1L) + Mockito.verify(db, Mockito.times(1)).deleteRecordsOlderThan(Mockito.anyLong()) + + // Exactly at the throttle boundary - the sweep runs again. + handler.maybePrunePersistedRecords(t0 + UploadHandler.PERSISTENCE_CLEANUP_INTERVAL_MILLIS) + Mockito.verify(db, Mockito.times(2)).deleteRecordsOlderThan(Mockito.anyLong()) + } + + @Test + fun testMaybePrunePersistedRecordsRetriesAfterFailure() { + val db = handler.mParticleDBManager + + // Phase 1 - deleteRecordsOlderThan reports failure (false); throttle must NOT be armed. + // This models the real contract: MParticleDBManager.deleteRecordsOlderThan catches + // SQL exceptions internally and signals failure via its return value. + Mockito.`when`(db.deleteRecordsOlderThan(Mockito.anyLong())).thenReturn(false) + val t1 = 1_000_000_000_000L + handler.maybePrunePersistedRecords(t1) + Mockito.verify(db, Mockito.times(1)).deleteRecordsOlderThan(Mockito.anyLong()) + + // Phase 2 - a minute later the sweep retries because the throttle was not armed. + handler.maybePrunePersistedRecords(t1 + 60_000L) + Mockito.verify(db, Mockito.times(2)).deleteRecordsOlderThan(Mockito.anyLong()) + + // Phase 3 - successful sweep arms the throttle. + Mockito.`when`(db.deleteRecordsOlderThan(Mockito.anyLong())).thenReturn(true) + handler.maybePrunePersistedRecords(t1 + 120_000L) + Mockito.verify(db, Mockito.times(3)).deleteRecordsOlderThan(Mockito.anyLong()) + + // Phase 4 - a minute after the successful sweep the throttle short-circuits the call. + handler.maybePrunePersistedRecords(t1 + 180_000L) + Mockito.verify(db, Mockito.times(3)).deleteRecordsOlderThan(Mockito.anyLong()) + } + @Test @Throws(Exception::class) fun testGetDeviceInfo() { From 5285149118b58cbd5cf4fb90cb58627deb7538b9 Mon Sep 17 00:00:00 2001 From: James Newman Date: Thu, 14 May 2026 11:20:57 -0400 Subject: [PATCH 09/25] feat: add customBaseURL CNAME support to NetworkOptions (#701) * feat: add customBaseURL CNAME support to NetworkOptions Adds NetworkOptions.Builder.setCustomBaseURL(String) which routes all mParticle endpoint traffic (config, events, identity, alias, audience) through a single HTTPS CNAME host. When set, customBaseURL takes priority over individual domain mappings and rewrites paths to match CDN routing: /config/v4/, /nativeevents/v2/, /identity/v1/, /nativeevents/v1/identity/, /nativeevents/v1//audience. Also adds R8 keep rules for MParticle$Internal and ConfigManager.getNetworkOptions() so kits can read customBaseURL after minification. The Rokt kit reads NetworkOptions.customBaseURL and forwards it to the Rokt SDK: https://github.com/mparticle-integrations/mparticle-android-integration-rokt/pull/143 Mirrors iOS work from mparticle-apple-sdk#760. Co-Authored-By: Claude Opus 4.7 (1M context) * fix: persist customBaseURL across NetworkOptions JSON round-trip NetworkOptions.toJson() and withNetworkOptions(String) did not include customBaseURL, so any value was silently dropped when UploadSettings serialized NetworkOptions to the upload database. Events and alias uploads read back NetworkOptions without customBaseURL and routed to the default mParticle endpoints instead of the partner CNAME. Also: - Extract the customBaseURL/DomainMapping host-resolution branch out of getUrl() into a private resolveHost() helper plus a small ResolvedHost value type, lowering getUrl()'s cyclomatic complexity. - Switch java.net.URL / java.net.MalformedURLException to imports. - Add two androidTest cases covering the round-trip. Co-Authored-By: Claude Opus 4.7 (1M context) * test(android-core): cover custom base URL upload storage --------- Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Thomson Thomas --- android-core/proguard.pro | 4 + .../database/services/UploadServiceTest.kt | 21 +++ .../networking/MParticleBaseClientImplTest.kt | 173 ++++++++++++++++++ .../networking/MParticleBaseClientImpl.java | 105 +++++++---- .../mparticle/networking/NetworkOptions.java | 64 ++++++- 5 files changed, 334 insertions(+), 33 deletions(-) diff --git a/android-core/proguard.pro b/android-core/proguard.pro index 400222184..956b9a715 100644 --- a/android-core/proguard.pro +++ b/android-core/proguard.pro @@ -84,6 +84,10 @@ -keep class com.mparticle.MPEvent$* { *; } -keep class com.mparticle.MParticle { *; } +-keep class com.mparticle.MParticle$Internal { *; } +-keep class com.mparticle.internal.ConfigManager { + public com.mparticle.networking.NetworkOptions getNetworkOptions(); +} -keep class com.mparticle.MParticle$EventType { *; } -keep class com.mparticle.MParticle$InstallType { *; } -keep class com.mparticle.MParticle$IdentityType { *; } diff --git a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/UploadServiceTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/UploadServiceTest.kt index 5b2ed9895..c0d3e5e38 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/UploadServiceTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/internal/database/services/UploadServiceTest.kt @@ -45,6 +45,27 @@ class UploadServiceTest : BaseMPServiceTest() { assertEquals(6, UploadService.getReadyUploads(database).size) } + @Test + @Throws(JSONException::class) + fun testUploadSettingsPreserveCustomBaseURL() { + val uploadSettings = UploadSettings( + "apiKey", + "secret", + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com:8443") + .build(), + "", + "", + ) + UploadService.insertUpload(database, uploadJson(System.currentTimeMillis()), uploadSettings) + + val readyUploads = UploadService.getReadyUploads(database) + + assertEquals(1, readyUploads.size) + assertEquals("rkt.example.com:8443", readyUploads[0].uploadSettings.networkOptions.customBaseURL) + } + @Throws(JSONException::class) private fun uploadJson(timestampMillis: Long): JSONObject = JSONObject() .put(Constants.MessageKey.TIMESTAMP, timestampMillis) diff --git a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt index 349a4faa4..d74f9f768 100644 --- a/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt +++ b/android-core/src/androidTest/kotlin/com.mparticle/networking/MParticleBaseClientImplTest.kt @@ -422,4 +422,177 @@ class MParticleBaseClientImplTest : BaseCleanInstallEachTest() { ) assertEquals(null, result) } + + @Test + @Throws(MalformedURLException::class) + fun testCustomBaseURLConfigEndpoint() { + val options = + MParticleOptions + .builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com") + .build(), + ).build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val configUrl = baseClientImpl.getUrl(MParticleBaseClientImpl.Endpoint.CONFIG) + Assert.assertTrue(configUrl.toString().contains("rkt.example.com/config/v4/")) + Assert.assertFalse(configUrl.toString().contains("config2.mparticle.com")) + } + + @Test + @Throws(MalformedURLException::class) + fun testCustomBaseURLEventsEndpoint() { + val options = + MParticleOptions + .builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com") + .build(), + ).build() + MParticle.start(options) + val uploadSettings = UploadSettings(apiKey, "secret", options.networkOptions, "", "") + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val eventsUrl = + baseClientImpl.getUrl(MParticleBaseClientImpl.Endpoint.EVENTS, null, null, uploadSettings) + Assert.assertTrue(eventsUrl.toString().contains("rkt.example.com/nativeevents/v2/")) + Assert.assertFalse(eventsUrl.toString().contains("nativesdks")) + } + + @Test + @Throws(MalformedURLException::class) + fun testCustomBaseURLIdentityEndpoint() { + val options = + MParticleOptions + .builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com") + .build(), + ).build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val identityUrl = + baseClientImpl.getUrl(MParticleBaseClientImpl.Endpoint.IDENTITY, "login") + Assert.assertTrue(identityUrl.toString().contains("rkt.example.com/identity/v1/login")) + Assert.assertFalse(identityUrl.toString().contains("identity.us1.mparticle.com")) + } + + @Test + @Throws(MalformedURLException::class) + fun testCustomBaseURLAliasEndpoint() { + val options = + MParticleOptions + .builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com") + .build(), + ).build() + MParticle.start(options) + val uploadSettings = UploadSettings(apiKey, "secret", options.networkOptions, "", "") + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val aliasUrl = + baseClientImpl.getUrl(MParticleBaseClientImpl.Endpoint.ALIAS, null, null, uploadSettings) + Assert.assertTrue(aliasUrl.toString().contains("rkt.example.com/nativeevents/v1/identity/")) + Assert.assertTrue(aliasUrl.toString().endsWith("/alias")) + Assert.assertFalse(aliasUrl.toString().contains("nativesdks")) + } + + @Test + @Throws(MalformedURLException::class) + fun testCustomBaseURLAudienceEndpoint() { + val options = + MParticleOptions + .builder(mContext) + .credentials(apiKey, "secret") + .networkOptions( + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com") + .build(), + ).build() + MParticle.start(options) + val baseClientImpl = AccessUtils.getApiClient() as MParticleBaseClientImpl + val audienceUrl = + baseClientImpl.getUrl(MParticleBaseClientImpl.Endpoint.AUDIENCE, 12345L) + Assert.assertTrue(audienceUrl.toString().contains("rkt.example.com/nativeevents/v1/")) + Assert.assertTrue(audienceUrl.toString().contains("/audience")) + Assert.assertFalse(audienceUrl.toString().contains("nativesdks")) + } + + @Test + fun testCustomBaseURLRejectsNonHTTPS() { + val opts = + NetworkOptions + .builder() + .setCustomBaseURL("http://rkt.example.com") + .build() + assertEquals(null, opts.customBaseURL) + } + + @Test + fun testCustomBaseURLStripsPath() { + val opts = + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com/some/path?q=1") + .build() + assertEquals("rkt.example.com", opts.customBaseURL) + } + + @Test + fun testCustomBaseURLPreservesPort() { + val opts = + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com:8443") + .build() + assertEquals("rkt.example.com:8443", opts.customBaseURL) + } + + @Test + fun testCustomBaseURLRejectsMalformed() { + val opts = + NetworkOptions + .builder() + .setCustomBaseURL("not a url") + .build() + assertEquals(null, opts.customBaseURL) + } + + @Test + fun testCustomBaseURLSurvivesJsonRoundTrip() { + val original = + NetworkOptions + .builder() + .setCustomBaseURL("https://rkt.example.com:8443") + .build() + Assert.assertEquals("rkt.example.com:8443", original.customBaseURL) + + val json = original.toJson().toString() + val restored = NetworkOptions.withNetworkOptions(json) + Assert.assertNotNull(restored) + Assert.assertEquals("rkt.example.com:8443", restored!!.customBaseURL) + } + + @Test + fun testCustomBaseURLOmittedFromJsonWhenUnset() { + val opts = NetworkOptions.builder().build() + val json = opts.toJson() + Assert.assertFalse(json.has("customBaseURL")) + + val restored = NetworkOptions.withNetworkOptions(json.toString()) + Assert.assertNull(restored!!.customBaseURL) + } } diff --git a/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java b/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java index 2ed7817ba..cbb2153e5 100644 --- a/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java +++ b/android-core/src/main/java/com/mparticle/networking/MParticleBaseClientImpl.java @@ -114,38 +114,26 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath, HashMap protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath,HashMap audienceQueryParams, @Nullable UploadSettings uploadSettings) throws MalformedURLException { NetworkOptions networkOptions = uploadSettings == null ? mConfigManager.getNetworkOptions() : uploadSettings.getNetworkOptions(); DomainMapping domainMapping = networkOptions.getDomain(endpoint); - String url = NetworkOptionsManager.getDefaultUrl(endpoint); String apiKey = uploadSettings == null ? mApiKey : uploadSettings.getApiKey(); + final boolean usingCustomBaseURL = !MPUtility.isEmpty(networkOptions.getCustomBaseURL()); - // `defaultDomain` variable is for URL generation when domain mapping is specified. - String defaultDomain = url; - boolean isDefaultDomain = true; - - // Check if domain mapping is specified and update the URL based on domain mapping - String domainMappingUrl = domainMapping != null ? domainMapping.getUrl() : null; - if (!MPUtility.isEmpty(domainMappingUrl)) { - isDefaultDomain = url.equals(domainMappingUrl); - url = domainMappingUrl; - } - - if (endpoint != Endpoint.CONFIG) { - // Set URL with pod prefix if it’s the default domain and endpoint is not CONFIG - if (isDefaultDomain) { - url = getPodUrl(url, mConfigManager.getPodPrefix(apiKey), mConfigManager.isDirectUrlRoutingEnabled()); - } else { - // When domain mapping is specified, generate the default domain. Whether podRedirection is enabled or not, always use the original URL. - defaultDomain = getPodUrl(defaultDomain, null, false); - } - } + ResolvedHost host = resolveHost(endpoint, networkOptions, domainMapping, apiKey); + String url = host.url; + String defaultDomain = host.defaultDomain; + boolean isDefaultDomain = host.isDefaultDomain; Uri uri; String subdirectory; String pathPrefix; String pathPostfix; boolean overridesSubdirectory = domainMapping != null && domainMapping.isOverridesSubdirectory(); + if (usingCustomBaseURL && overridesSubdirectory) { + Logger.warning("NetworkOptions: customBaseURL with overridesSubdirectory is unsupported for CDN routing; overridesSubdirectory will be ignored for " + endpoint.name() + "."); + overridesSubdirectory = false; + } switch (endpoint) { case CONFIG: - pathPrefix = SERVICE_VERSION_4 + "/"; + pathPrefix = usingCustomBaseURL ? "/config/v4/" : SERVICE_VERSION_4 + "/"; subdirectory = overridesSubdirectory ? "" : pathPrefix; pathPostfix = mApiKey + "/config"; Uri.Builder builder = new Uri.Builder() @@ -165,9 +153,9 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath,HashMap< } } } - return MPUrl.getUrl(builder.build().toString(), generateDefaultURL(isDefaultDomain, builder.build(), defaultDomain, (pathPrefix + pathPostfix))); + return MPUrl.getUrl(builder.build().toString(), generateDefaultURL(isDefaultDomain, builder.build(), defaultDomain, (SERVICE_VERSION_4 + "/" + pathPostfix))); case EVENTS: - pathPrefix = SERVICE_VERSION_2 + "/"; + pathPrefix = usingCustomBaseURL ? "/nativeevents/v2/" : SERVICE_VERSION_2 + "/"; subdirectory = overridesSubdirectory ? "" : pathPrefix; pathPostfix = apiKey + "/events"; uri = new Uri.Builder() @@ -176,9 +164,9 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath,HashMap< .path(subdirectory + pathPostfix) .build(); - return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (pathPrefix + pathPostfix))); + return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (SERVICE_VERSION_2 + "/" + pathPostfix))); case ALIAS: - pathPrefix = SERVICE_VERSION_1 + "/identity/"; + pathPrefix = usingCustomBaseURL ? "/nativeevents/v1/identity/" : SERVICE_VERSION_1 + "/identity/"; subdirectory = overridesSubdirectory ? "" : pathPrefix; pathPostfix = apiKey + "/alias"; uri = new Uri.Builder() @@ -186,26 +174,28 @@ protected MPUrl getUrl(Endpoint endpoint, @Nullable String identityPath,HashMap< .encodedAuthority(url) .path(subdirectory + pathPostfix) .build(); - return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (pathPrefix + pathPostfix))); + return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (SERVICE_VERSION_1 + "/identity/" + pathPostfix))); case IDENTITY: - pathPrefix = SERVICE_VERSION_1 + "/"; - subdirectory = overridesSubdirectory ? "" : SERVICE_VERSION_1 + "/"; + pathPrefix = usingCustomBaseURL ? "/identity/v1/" : SERVICE_VERSION_1 + "/"; + subdirectory = overridesSubdirectory ? "" : pathPrefix; pathPostfix = identityPath; uri = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) .path(subdirectory + pathPostfix) .build(); - return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (pathPrefix + pathPostfix))); + return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (SERVICE_VERSION_1 + "/" + pathPostfix))); case AUDIENCE: - pathPostfix = SERVICE_VERSION_1 + "/" + mApiKey + "/audience"; + pathPostfix = usingCustomBaseURL + ? "/nativeevents/v1/" + mApiKey + "/audience" + : SERVICE_VERSION_1 + "/" + mApiKey + "/audience"; uri = new Uri.Builder() .scheme(BuildConfig.SCHEME) .encodedAuthority(url) .path(pathPostfix) .appendQueryParameter("mpid", String.valueOf(mConfigManager.getMpid())) .build(); - return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, pathPostfix)); + return MPUrl.getUrl(uri.toString(), generateDefaultURL(isDefaultDomain, uri, defaultDomain, (SERVICE_VERSION_1 + "/" + mApiKey + "/audience"))); default: return null; } @@ -246,6 +236,57 @@ String getPodUrl(String URLPrefix, String pod, boolean enablePodRedirection) { return null; } + /** + * Resolves the host(s) used to build the endpoint URL. Returns three values: + * {@code url} — host used for the request, {@code defaultDomain} — host used as the + * fallback for {@link #generateDefaultURL}, and {@code isDefaultDomain} — true when + * {@code url} is the unmodified mParticle default. + * + *

Priority: {@code customBaseURL} → per-endpoint {@code DomainMapping} → default. + */ + private ResolvedHost resolveHost(Endpoint endpoint, NetworkOptions networkOptions, DomainMapping domainMapping, String apiKey) { + String defaultUrl = NetworkOptionsManager.getDefaultUrl(endpoint); + String customBaseURL = networkOptions.getCustomBaseURL(); + + if (!MPUtility.isEmpty(customBaseURL)) { + if (domainMapping != null && !MPUtility.isEmpty(domainMapping.getUrl())) { + Logger.warning("NetworkOptions: customBaseURL is set; domain mapping for " + endpoint.name() + " is ignored."); + } + // When custom CNAME is used, the default-domain URL still needs the pod prefix + // so MPConnectionTest matching and pinning fallbacks continue to work. + String defaultDomain = endpoint == Endpoint.CONFIG ? defaultUrl : getPodUrl(defaultUrl, null, false); + return new ResolvedHost(customBaseURL, defaultDomain, false); + } + + String domainMappingUrl = domainMapping != null ? domainMapping.getUrl() : null; + boolean isDefaultDomain = MPUtility.isEmpty(domainMappingUrl) || defaultUrl.equals(domainMappingUrl); + String url = isDefaultDomain ? defaultUrl : domainMappingUrl; + String defaultDomain = defaultUrl; + + if (endpoint != Endpoint.CONFIG) { + if (isDefaultDomain) { + // Default domain gets the pod prefix. + url = getPodUrl(url, mConfigManager.getPodPrefix(apiKey), mConfigManager.isDirectUrlRoutingEnabled()); + } else { + // Domain-mapped: always generate the default with the original (un-pod-prefixed) host. + defaultDomain = getPodUrl(defaultDomain, null, false); + } + } + return new ResolvedHost(url, defaultDomain, isDefaultDomain); + } + + private static final class ResolvedHost { + final String url; + final String defaultDomain; + final boolean isDefaultDomain; + + ResolvedHost(String url, String defaultDomain, boolean isDefaultDomain) { + this.url = url; + this.defaultDomain = defaultDomain; + this.isDefaultDomain = isDefaultDomain; + } + } + public enum Endpoint { CONFIG(1), IDENTITY(2), diff --git a/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java b/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java index 6faf9d521..708aae363 100644 --- a/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java +++ b/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java @@ -16,11 +16,13 @@ import org.json.JSONException; import org.json.JSONObject; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.HashSet; import java.util.Set; public class NetworkOptions { @@ -28,6 +30,7 @@ public class NetworkOptions { Map domainMappings = new HashMap(); boolean pinningDisabledInDevelopment = false; boolean pinningDisabled = false; + private String customBaseURL = null; private static Set loggedDomainTypes = new HashSet<>(); private NetworkOptions() { @@ -44,6 +47,10 @@ private NetworkOptions(Builder builder) { if (builder.pinningDisabled != null) { pinningDisabled = builder.pinningDisabled; } + + if (builder.customBaseURL != null) { + customBaseURL = builder.customBaseURL; + } } @NonNull @@ -61,6 +68,13 @@ public static NetworkOptions withNetworkOptions(@Nullable String jsonString) { JSONObject jsonObject = new JSONObject(jsonString); builder.setPinningDisabledInDevelopment(jsonObject.optBoolean("disableDevPinning", false)); builder.setPinningDisabled(jsonObject.optBoolean("disablePinning", false)); + String storedCustomBaseURL = jsonObject.optString("customBaseURL", null); + if (!MPUtility.isEmpty(storedCustomBaseURL)) { + // Stored value is already host(:port). Skip the Builder setter (which expects a full + // HTTPS URL with scheme) and assign directly so a previously-validated value survives + // the JSON round-trip. + builder.customBaseURL = storedCustomBaseURL; + } JSONArray domainMappingsJson = jsonObject.getJSONArray("domainMappings"); for (int i = 0; i < domainMappingsJson.length(); i++) { builder.addDomainMapping(DomainMapping @@ -106,6 +120,16 @@ public boolean isPinningDisabled() { return pinningDisabled; } + /** + * Returns the configured custom CNAME host (without scheme), or {@code null} + * if not set. When non-null, this host overrides individual domain mappings + * for all endpoints. + */ + @Nullable + public String getCustomBaseURL() { + return customBaseURL; + } + DomainMapping getDomain(Endpoint endpoint) { return domainMappings.get(endpoint); } @@ -123,6 +147,9 @@ public JSONObject toJson() { JSONArray domainMappingsJson = new JSONArray(); networkOptions.put("disableDevPinning", pinningDisabledInDevelopment); networkOptions.put("disablePinning", pinningDisabled); + if (!MPUtility.isEmpty(customBaseURL)) { + networkOptions.put("customBaseURL", customBaseURL); + } networkOptions.put("domainMappings", domainMappingsJson); for (DomainMapping domainMapping : domainMappings.values()) { domainMappingsJson.put(domainMapping.toString()); @@ -137,6 +164,7 @@ public static class Builder { private Map domainMappings = new HashMap(); private Boolean pinningDisabledInDevelopment; private Boolean pinningDisabled; + private String customBaseURL; private Builder() { } @@ -187,6 +215,40 @@ public Builder setPinningDisabled(boolean disabled) { return this; } + /** + * Routes all mParticle endpoint traffic (config, events, identity, alias, audience) + * through a single CNAME host. Must be an HTTPS URL (e.g. https://rkt.example.com). + * Non-HTTPS values are rejected with a warning log and the property is left unset. + * + *

When set, this property takes priority over any per-endpoint domain mapping. + * Any path, query, or fragment on the URL is ignored — only the scheme, host, and + * port are used. + * + *

Certificate pinning: if pinning is enabled (default), supply certificates for + * the CNAME domain via the relevant {@link DomainMapping}, or disable pinning via + * {@link #setPinningDisabled(boolean)} / {@link #setPinningDisabledInDevelopment(boolean)}. + * + * @param customBaseURL HTTPS URL containing the CNAME host + */ + @NonNull + public Builder setCustomBaseURL(@NonNull String customBaseURL) { + try { + URL parsed = new URL(customBaseURL); + if (!"https".equalsIgnoreCase(parsed.getProtocol()) || MPUtility.isEmpty(parsed.getHost())) { + Logger.warning("NetworkOptions: customBaseURL must use HTTPS and include a valid host — value ignored."); + return this; + } + StringBuilder host = new StringBuilder(parsed.getHost()); + if (parsed.getPort() != -1) { + host.append(":").append(parsed.getPort()); + } + this.customBaseURL = host.toString(); + } catch (MalformedURLException e) { + Logger.warning("NetworkOptions: customBaseURL is malformed — value ignored."); + } + return this; + } + @NonNull public NetworkOptions build() { return new NetworkOptions(this); From 59a4a9a68e3addfaf5c4eea1afd396889721819d Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Thu, 14 May 2026 15:43:01 +0000 Subject: [PATCH 10/25] chore: Update submodules --- kits/appboy-kit | 2 +- kits/rokt-kit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kits/appboy-kit b/kits/appboy-kit index 84f7f64fb..f44d2b3a5 160000 --- a/kits/appboy-kit +++ b/kits/appboy-kit @@ -1 +1 @@ -Subproject commit 84f7f64fb24902ee0717053d0cdb1ca4e31599d6 +Subproject commit f44d2b3a5fb29266e2a72b5ed0d32c95ced1c62f diff --git a/kits/rokt-kit b/kits/rokt-kit index b96f92867..ff28ccdc3 160000 --- a/kits/rokt-kit +++ b/kits/rokt-kit @@ -1 +1 @@ -Subproject commit b96f92867ab538673a648b2ecf863598441d66aa +Subproject commit ff28ccdc3d513044663d9c7b407e8e5ea3ed9338 From 3593cdcbbf1910be124f9e327eeff46177fb2ce0 Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Thu, 14 May 2026 15:53:43 +0000 Subject: [PATCH 11/25] chore: 5.79.0 (release) ## [5.79.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.5...v5.79.0) (2026-05-14) ### Features * add customBaseURL CNAME support to NetworkOptions ([#701](https://github.com/mParticle/mparticle-android-sdk/issues/701)) ([5285149](https://github.com/mParticle/mparticle-android-sdk/commit/5285149118b58cbd5cf4fb90cb58627deb7538b9)), closes [mparticle-apple-sdk#760](https://github.com/mParticle/mparticle-apple-sdk/issues/760) * Add max persistence age override option [TRIAGE-608] ([#699](https://github.com/mParticle/mparticle-android-sdk/issues/699)) ([ca88322](https://github.com/mParticle/mparticle-android-sdk/commit/ca88322ac7ef8649ed111cf28afb81d99cdb271a)) ### Bug Fixes * increase MPLatch timeout from 5s to 30s ([#695](https://github.com/mParticle/mparticle-android-sdk/issues/695)) ([20f723f](https://github.com/mParticle/mparticle-android-sdk/commit/20f723f27cf296c879ceebc9d234edca039ab371)) ### Updates & Maintenance * Update submodules ([59a4a9a](https://github.com/mParticle/mparticle-android-sdk/commit/59a4a9a68e3addfaf5c4eea1afd396889721819d)) --- CHANGELOG.md | 18 ++++++++++++++++++ README.md | 6 +++--- build.gradle | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd0d2cbce..f039bb5b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## [5.79.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.5...v5.79.0) (2026-05-14) + + +### Features + +* add customBaseURL CNAME support to NetworkOptions ([#701](https://github.com/mParticle/mparticle-android-sdk/issues/701)) ([5285149](https://github.com/mParticle/mparticle-android-sdk/commit/5285149118b58cbd5cf4fb90cb58627deb7538b9)), closes [mparticle-apple-sdk#760](https://github.com/mParticle/mparticle-apple-sdk/issues/760) +* Add max persistence age override option [TRIAGE-608] ([#699](https://github.com/mParticle/mparticle-android-sdk/issues/699)) ([ca88322](https://github.com/mParticle/mparticle-android-sdk/commit/ca88322ac7ef8649ed111cf28afb81d99cdb271a)) + + +### Bug Fixes + +* increase MPLatch timeout from 5s to 30s ([#695](https://github.com/mParticle/mparticle-android-sdk/issues/695)) ([20f723f](https://github.com/mParticle/mparticle-android-sdk/commit/20f723f27cf296c879ceebc9d234edca039ab371)) + + +### Updates & Maintenance + +* Update submodules ([59a4a9a](https://github.com/mParticle/mparticle-android-sdk/commit/59a4a9a68e3addfaf5c4eea1afd396889721819d)) + ## [5.78.5](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.4...v5.78.5) (2026-03-25) diff --git a/README.md b/README.md index a0a16d657..6321b413a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can grab the Core SDK via Maven Central. Please see the badge above and foll ```groovy dependencies { - implementation 'com.mparticle:android-core:5.78.5' + implementation 'com.mparticle:android-core:5.79.0' } ``` @@ -31,8 +31,8 @@ Several integrations require additional client-side add-on libraries called "kit ```groovy dependencies { implementation ( - 'com.mparticle:android-example-kit:5.78.5', - 'com.mparticle:android-another-kit:5.78.5' + 'com.mparticle:android-example-kit:5.79.0', + 'com.mparticle:android-another-kit:5.79.0' ) } ``` diff --git a/build.gradle b/build.gradle index 74315ca09..bef6ec38f 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ subprojects { allprojects { group = 'com.mparticle' - version = '5.78.5-SNAPSHOT' + version = '5.79.0-SNAPSHOT' if (project.hasProperty('isRelease') && project.isRelease) { version = version.toString().replace("-SNAPSHOT", "") } From f4ceca7de2c2ba01ea87c98698ef7bb84991b4ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 09:26:13 -0400 Subject: [PATCH 12/25] chore: bump trunk-io/trunk-action from 1.2.4 to 1.3.1 (#705) Bumps [trunk-io/trunk-action](https://github.com/trunk-io/trunk-action) from 1.2.4 to 1.3.1. - [Release notes](https://github.com/trunk-io/trunk-action/releases) - [Commits](https://github.com/trunk-io/trunk-action/compare/75699af9e26881e564e9d832ef7dc3af25ec031b...04ba50e7658c81db7356da96657e6e77f220bfa3) --- updated-dependencies: - dependency-name: trunk-io/trunk-action dependency-version: 1.3.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 81b0da6d0..fe063da8e 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 #v5.0.0 - name: Trunk Check - uses: trunk-io/trunk-action@75699af9e26881e564e9d832ef7dc3af25ec031b # v1.2.4 + uses: trunk-io/trunk-action@04ba50e7658c81db7356da96657e6e77f220bfa3 # v1.3.1 with: check-mode: pull_request pr-check-hadcoded-secrets: From 491f169d8cbb39d8f1828ff71309bdec4918c354 Mon Sep 17 00:00:00 2001 From: ankitsingh08 Date: Wed, 20 May 2026 14:15:48 +0100 Subject: [PATCH 13/25] Handle legacy persisted domain mappings safely (#703) Co-authored-by: James Newman --- .../mparticle/networking/DomainMapping.java | 4 +- .../mparticle/networking/NetworkOptions.java | 9 ++-- .../networking/NetworkOptionsTest.kt | 44 +++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/android-core/src/main/java/com/mparticle/networking/DomainMapping.java b/android-core/src/main/java/com/mparticle/networking/DomainMapping.java index af908cd43..80ac6a42a 100644 --- a/android-core/src/main/java/com/mparticle/networking/DomainMapping.java +++ b/android-core/src/main/java/com/mparticle/networking/DomainMapping.java @@ -326,7 +326,7 @@ private static Builder withJson(String jsonString) { JSONObject jsonObject = new JSONObject(jsonString); int type = jsonObject.getInt("mType"); String newUrl = jsonObject.getString("url"); - boolean overridesSubdirectory = jsonObject.getBoolean("overridesSubdirectory"); + boolean overridesSubdirectory = jsonObject.optBoolean("overridesSubdirectory", false); Builder builder = new Builder(Endpoint.parseInt(type), newUrl, overridesSubdirectory); JSONArray certificatesJsonArray = jsonObject.getJSONArray("mCertificates"); for (int i = 0; i < certificatesJsonArray.length(); i++) { @@ -339,4 +339,4 @@ private static Builder withJson(String jsonString) { return null; } } -} \ No newline at end of file +} diff --git a/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java b/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java index 708aae363..678f66529 100644 --- a/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java +++ b/android-core/src/main/java/com/mparticle/networking/NetworkOptions.java @@ -77,9 +77,12 @@ public static NetworkOptions withNetworkOptions(@Nullable String jsonString) { } JSONArray domainMappingsJson = jsonObject.getJSONArray("domainMappings"); for (int i = 0; i < domainMappingsJson.length(); i++) { - builder.addDomainMapping(DomainMapping - .withDomainMapping(domainMappingsJson.getString(i)) - .build()); + DomainMapping.Builder domainMappingBuilder = DomainMapping.withDomainMapping(domainMappingsJson.getString(i)); + if (domainMappingBuilder == null) { + Logger.warning("NetworkOptions: skipping invalid persisted domain mapping at index " + i + "."); + continue; + } + builder.addDomainMapping(domainMappingBuilder.build()); } } catch (Exception e) { Logger.error(e); diff --git a/android-core/src/test/kotlin/com/mparticle/networking/NetworkOptionsTest.kt b/android-core/src/test/kotlin/com/mparticle/networking/NetworkOptionsTest.kt index 5f0f688dd..4b0039ad2 100644 --- a/android-core/src/test/kotlin/com/mparticle/networking/NetworkOptionsTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/networking/NetworkOptionsTest.kt @@ -47,6 +47,50 @@ class NetworkOptionsTest { Assert.assertTrue(equals(options, optionsDeserialized)) } + @Test + fun testLegacyDomainMappingWithoutOverridesSubdirectoryParses() { + val options = + NetworkOptions.withNetworkOptions( + """ + { + "disableDevPinning": false, + "disablePinning": false, + "domainMappings": [ + "{\"mType\":1,\"url\":\"www.configUrl.com\",\"mCertificates\":[]}" + ] + } + """.trimIndent(), + ) + + Assert.assertNotNull(options) + Assert.assertNotNull(options!!.configDomain) + Assert.assertFalse(options.configDomain!!.isOverridesSubdirectory) + Assert.assertEquals("www.configUrl.com", options.configDomain!!.url) + } + + @Test + fun testNetworkOptionsSkipsInvalidPersistedDomainMappings() { + val options = + NetworkOptions.withNetworkOptions( + """ + { + "disableDevPinning": false, + "disablePinning": false, + "domainMappings": [ + "{\"mType\":1,\"url\":\"www.configUrl.com\",\"overridesSubdirectory\":true,\"mCertificates\":[]}", + "{\"mType\":1,\"url\":\"www.invalid.com\"" + ] + } + """.trimIndent(), + ) + + Assert.assertNotNull(options) + Assert.assertNotNull(options!!.configDomain) + Assert.assertEquals(1, options.domainMappings.size) + Assert.assertTrue(options.configDomain!!.isOverridesSubdirectory) + Assert.assertEquals("www.configUrl.com", options.configDomain!!.url) + } + companion object { fun equals( networkOptions1: NetworkOptions, From eff10ead69167f0ea3df248b2bbed089397600cd Mon Sep 17 00:00:00 2001 From: James Newman Date: Wed, 27 May 2026 17:09:47 -0400 Subject: [PATCH 14/25] fix(ci): pin isolated-kit gradle version to prevent 6.0.0-rc.1 pull (#714) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The isolated urbanairship-kit lint/test steps in daily.yml and the kit-compatibility test in pull-request.yml run `./gradlew` from inside the kit dir without `-Pversion=...`. The kit's standalone build.gradle defaults `project.version = '+'`, so its `com.mparticle:android-kit-plugin` dependency resolves to the highest version on Maven Central — now `6.0.0-rc.1` (published 2026-05-22), which renamed/removed kit-base symbols and broke the kit's compile. Mirror the pattern already used in the Sonatype Release job: extract the SDK version via `./gradlew -q properties` and pass it as `-Pversion=...` to every standalone kit invocation, so it resolves the freshly-published mavenLocal artifact instead of the RC. Affected steps: - lint-checks → Run Isolated Kit Lint (urbanairship-kit) - kotlin-lint-checks → Run Isolated Kit Kotlin Lint (urbanairship-kit) - update-kits → Test Isolated Kits (urbanairship-kit) - kit-compatibility-test → Run Isolated Kit Compatibility Tests (urbanairship-kit) The companion defensive fix in mparticle-android-integration-urbanairship pins the kit's standalone `project.version` to `[5.0,6.0)`. Co-authored-by: Claude Opus 4.7 (1M context) --- .github/workflows/daily.yml | 15 ++++++++++++--- .github/workflows/pull-request.yml | 5 ++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index b15a2a642..bbd206621 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -245,9 +245,12 @@ jobs: run: ./gradlew publishReleaseLocal - name: "Run Android Kit Lint" run: ./gradlew publishReleaseLocal -c settings-kits.gradle lint + - name: "Get SDK version for isolated kits" + id: sdk-version + run: echo "version=$(./gradlew -q properties | grep '^version:' | awk '{print $2}')" >> $GITHUB_OUTPUT - name: "Run Isolated Kit Lint (urbanairship-kit)" working-directory: kits/urbanairship-kit - run: ./gradlew lint + run: ./gradlew -Pversion=${{ steps.sdk-version.outputs.version }} lint - name: "Archive Test Results" uses: actions/upload-artifact@v7 if: always() @@ -284,9 +287,12 @@ jobs: run: ./gradlew publishReleaseLocal - name: "Run Android Kit Kotlin Lint" run: ./gradlew publishReleaseLocal -c settings-kits.gradle ktlintCheck + - name: "Get SDK version for isolated kits" + id: sdk-version + run: echo "version=$(./gradlew -q properties | grep '^version:' | awk '{print $2}')" >> $GITHUB_OUTPUT - name: "Run Isolated Kit Kotlin Lint (urbanairship-kit)" working-directory: kits/urbanairship-kit - run: ./gradlew ktlintCheck + run: ./gradlew -Pversion=${{ steps.sdk-version.outputs.version }} ktlintCheck - name: "Archive Test Results" uses: actions/upload-artifact@v7 if: always() @@ -326,9 +332,12 @@ jobs: run: ./gradlew -PisRelease=true clean publishReleaseLocal - name: "Test Kits" run: ./gradlew -PisRelease=true clean testRelease publishReleaseLocal -c settings-kits.gradle + - name: "Get SDK version for isolated kits" + id: sdk-version + run: echo "version=$(./gradlew -PisRelease=true -q properties | grep '^version:' | awk '{print $2}')" >> $GITHUB_OUTPUT - name: "Test Isolated Kits (urbanairship-kit)" working-directory: kits/urbanairship-kit - run: ./gradlew -PisRelease=true clean testRelease publishReleaseLocal + run: ./gradlew -PisRelease=true -Pversion=${{ steps.sdk-version.outputs.version }} clean testRelease publishReleaseLocal semantic-release-dryrun: name: "Test Semantic Release - Dry Run" diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index fe063da8e..75160efb3 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -254,9 +254,12 @@ jobs: run: ./gradlew -PisRelease=true :android-kit-base:testRelease - name: "Run Kit Release Tests and Build" run: ./gradlew -PisRelease=true -p kits testRelease -c ../settings-kits.gradle + - name: "Get SDK version for isolated kits" + id: sdk-version + run: echo "version=$(./gradlew -PisRelease=true -q properties | grep '^version:' | awk '{print $2}')" >> $GITHUB_OUTPUT - name: "Run Isolated Kit Compatibility Tests (urbanairship-kit)" working-directory: kits/urbanairship-kit - run: ./gradlew -PisRelease=true testRelease + run: ./gradlew -PisRelease=true -Pversion=${{ steps.sdk-version.outputs.version }} testRelease automerge-dependabot: name: "Save PR Number for Dependabot Automerge" From e435979e1e966ef2b318bb29135852f9d55df55a Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Wed, 27 May 2026 21:12:52 +0000 Subject: [PATCH 15/25] chore: Update submodules --- kits/adjust-kit | 2 +- kits/adobe-kit | 2 +- kits/adobemedia-kit | 2 +- kits/appboy-kit | 2 +- kits/appsflyer-kit | 2 +- kits/apptentive-kit | 2 +- kits/apptimize-kit | 2 +- kits/apteligent-kit | 2 +- kits/branch-kit | 2 +- kits/button-kit | 2 +- kits/clevertap-kit | 2 +- kits/comscore-kit | 2 +- kits/flurry-kit | 2 +- kits/foresee-kit | 2 +- kits/googleanalyticsfirebase-kit | 2 +- kits/googleanalyticsfirebasega4-kit | 2 +- kits/iterable-kit | 2 +- kits/kochava-kit | 2 +- kits/leanplum-kit | 2 +- kits/localytics-kit | 2 +- kits/onetrust-kit | 2 +- kits/optimizely-kit | 2 +- kits/pilgrim-kit | 2 +- kits/radar-kit | 2 +- kits/responsys-kit | 2 +- kits/revealmobile-kit | 2 +- kits/rokt-kit | 2 +- kits/singular-kit | 2 +- kits/skyhook-kit | 2 +- kits/taplytics-kit | 2 +- kits/tune-kit | 2 +- kits/urbanairship-kit | 2 +- kits/wootric-kit | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/kits/adjust-kit b/kits/adjust-kit index a9d435738..a66b47146 160000 --- a/kits/adjust-kit +++ b/kits/adjust-kit @@ -1 +1 @@ -Subproject commit a9d4357385e4a417e2bf4d2ac15fbd85e1cb7db7 +Subproject commit a66b47146100d32ed497b760d5e776125383a3a8 diff --git a/kits/adobe-kit b/kits/adobe-kit index 53a816d8a..f149b654e 160000 --- a/kits/adobe-kit +++ b/kits/adobe-kit @@ -1 +1 @@ -Subproject commit 53a816d8a7bbb756d252a0b54b72a4308f704d80 +Subproject commit f149b654e19086e7f1e16e6a34401a3922efc647 diff --git a/kits/adobemedia-kit b/kits/adobemedia-kit index 9ee884afb..bed25f5bd 160000 --- a/kits/adobemedia-kit +++ b/kits/adobemedia-kit @@ -1 +1 @@ -Subproject commit 9ee884afb53b715c92b89486bad3b55b44d47268 +Subproject commit bed25f5bd4b80445618b59ce57d84beb6d508d97 diff --git a/kits/appboy-kit b/kits/appboy-kit index f44d2b3a5..4fd583e67 160000 --- a/kits/appboy-kit +++ b/kits/appboy-kit @@ -1 +1 @@ -Subproject commit f44d2b3a5fb29266e2a72b5ed0d32c95ced1c62f +Subproject commit 4fd583e67b4b9688d383a8dfed1c315957ba35c8 diff --git a/kits/appsflyer-kit b/kits/appsflyer-kit index e58e6e00b..84e55b8fa 160000 --- a/kits/appsflyer-kit +++ b/kits/appsflyer-kit @@ -1 +1 @@ -Subproject commit e58e6e00baae4ce863e2ef38da46a2746addbdc1 +Subproject commit 84e55b8fae69a07f091034fb530b87f5924adb7d diff --git a/kits/apptentive-kit b/kits/apptentive-kit index 09a489e3f..658cb9112 160000 --- a/kits/apptentive-kit +++ b/kits/apptentive-kit @@ -1 +1 @@ -Subproject commit 09a489e3ff50620cac902d89cbaee4b93e92096f +Subproject commit 658cb9112df0bde4c219c5057441ffc8a6308a79 diff --git a/kits/apptimize-kit b/kits/apptimize-kit index 8de020118..cf998297b 160000 --- a/kits/apptimize-kit +++ b/kits/apptimize-kit @@ -1 +1 @@ -Subproject commit 8de020118e1cea732749fcdf7cd6b88a255e5be6 +Subproject commit cf998297bd50c59c8cba3af08f2a4b02200acde2 diff --git a/kits/apteligent-kit b/kits/apteligent-kit index 91afc5c1c..3a672a770 160000 --- a/kits/apteligent-kit +++ b/kits/apteligent-kit @@ -1 +1 @@ -Subproject commit 91afc5c1c2b84bb331d9c5a38600404e9862e86a +Subproject commit 3a672a770668a38b6e641f0b217d1bb68aecd912 diff --git a/kits/branch-kit b/kits/branch-kit index 22f5de5b4..3511c3183 160000 --- a/kits/branch-kit +++ b/kits/branch-kit @@ -1 +1 @@ -Subproject commit 22f5de5b4cc892cbbd6fa9f65f922eebdd8a47ef +Subproject commit 3511c318337952bd80c880a02ad6fd6ed84441fb diff --git a/kits/button-kit b/kits/button-kit index 5155ed507..444feb68e 160000 --- a/kits/button-kit +++ b/kits/button-kit @@ -1 +1 @@ -Subproject commit 5155ed5075a4f3410988bebaf872db1ebde8e579 +Subproject commit 444feb68e7e8f4514337ae679adb4092be3f04c8 diff --git a/kits/clevertap-kit b/kits/clevertap-kit index e2750d1c9..20a9c6b47 160000 --- a/kits/clevertap-kit +++ b/kits/clevertap-kit @@ -1 +1 @@ -Subproject commit e2750d1c9c6755a7880a197abcd87cdef26f4cf3 +Subproject commit 20a9c6b47722dcf030a16c763371ca66407ae587 diff --git a/kits/comscore-kit b/kits/comscore-kit index b57adeb04..d1993ed0b 160000 --- a/kits/comscore-kit +++ b/kits/comscore-kit @@ -1 +1 @@ -Subproject commit b57adeb041ea2fb9d95b2fcd3c644bf58700d676 +Subproject commit d1993ed0bd478e1f2b2bd712002945f910666301 diff --git a/kits/flurry-kit b/kits/flurry-kit index c335775fe..966998193 160000 --- a/kits/flurry-kit +++ b/kits/flurry-kit @@ -1 +1 @@ -Subproject commit c335775fe96534ac3b03fd4a4dba32f3b2216099 +Subproject commit 966998193366e142c373997f58ca10bb95155eee diff --git a/kits/foresee-kit b/kits/foresee-kit index 3c7003cf5..871788033 160000 --- a/kits/foresee-kit +++ b/kits/foresee-kit @@ -1 +1 @@ -Subproject commit 3c7003cf5545e1a5a21fd3effb5d782854302bf1 +Subproject commit 87178803394f754062b999379eed63358e935800 diff --git a/kits/googleanalyticsfirebase-kit b/kits/googleanalyticsfirebase-kit index 4217ef83c..e8e02eb74 160000 --- a/kits/googleanalyticsfirebase-kit +++ b/kits/googleanalyticsfirebase-kit @@ -1 +1 @@ -Subproject commit 4217ef83c4045b1c7f7f61030b8d212a3b437133 +Subproject commit e8e02eb74533dbd0e7ed90b1972a61db64542771 diff --git a/kits/googleanalyticsfirebasega4-kit b/kits/googleanalyticsfirebasega4-kit index 93bfe8be4..fae959c94 160000 --- a/kits/googleanalyticsfirebasega4-kit +++ b/kits/googleanalyticsfirebasega4-kit @@ -1 +1 @@ -Subproject commit 93bfe8be4a6b2310207d735a412f5797de6713c4 +Subproject commit fae959c9497e1c6949982ca373cbadd49fd506b8 diff --git a/kits/iterable-kit b/kits/iterable-kit index c94c7ab09..2b9a440f9 160000 --- a/kits/iterable-kit +++ b/kits/iterable-kit @@ -1 +1 @@ -Subproject commit c94c7ab09be031bd6103ddb6480d6453755e1c1e +Subproject commit 2b9a440f9f4661223f9fa16218028a954c1a8e30 diff --git a/kits/kochava-kit b/kits/kochava-kit index 65855e512..15a062fa7 160000 --- a/kits/kochava-kit +++ b/kits/kochava-kit @@ -1 +1 @@ -Subproject commit 65855e51269c1a472d2ac105ba305c6a2be4b8ad +Subproject commit 15a062fa7f8735f7d52c47d622a5d4d976319cec diff --git a/kits/leanplum-kit b/kits/leanplum-kit index dc2fa8288..c7453baa0 160000 --- a/kits/leanplum-kit +++ b/kits/leanplum-kit @@ -1 +1 @@ -Subproject commit dc2fa8288a1b1d987fee0c8276d3671259b73cbd +Subproject commit c7453baa0b89050e938f7541971bfa64fa24b3c5 diff --git a/kits/localytics-kit b/kits/localytics-kit index bc95677fe..c89ef9b94 160000 --- a/kits/localytics-kit +++ b/kits/localytics-kit @@ -1 +1 @@ -Subproject commit bc95677fe0459003a1a9736a2a1061ba98f1b432 +Subproject commit c89ef9b94818004aff9bae46c25ea33582ae9adb diff --git a/kits/onetrust-kit b/kits/onetrust-kit index e29e1cd27..0fed4ec6a 160000 --- a/kits/onetrust-kit +++ b/kits/onetrust-kit @@ -1 +1 @@ -Subproject commit e29e1cd273645db89d94d4c6d809d15d600f3b7f +Subproject commit 0fed4ec6a0a66a8e7b752b8a67272fa8d7dc4c88 diff --git a/kits/optimizely-kit b/kits/optimizely-kit index 2b291d61e..200e280eb 160000 --- a/kits/optimizely-kit +++ b/kits/optimizely-kit @@ -1 +1 @@ -Subproject commit 2b291d61ea0c4c8899f45dcfddc47d1e3603f3be +Subproject commit 200e280eb219ebe0b36673f19f11b495821074a5 diff --git a/kits/pilgrim-kit b/kits/pilgrim-kit index 3431c8081..ae3bb05bb 160000 --- a/kits/pilgrim-kit +++ b/kits/pilgrim-kit @@ -1 +1 @@ -Subproject commit 3431c808125239361ccd708c41782510613955d6 +Subproject commit ae3bb05bb9866a85e4578b1fb9d7e78fc4a9e6a2 diff --git a/kits/radar-kit b/kits/radar-kit index 0a31841b8..b33c069dc 160000 --- a/kits/radar-kit +++ b/kits/radar-kit @@ -1 +1 @@ -Subproject commit 0a31841b8099ddac1a4f96a508ab0b0020d08b9a +Subproject commit b33c069dc0baa9396eaa317060c08cc9cad67d76 diff --git a/kits/responsys-kit b/kits/responsys-kit index 62c6e15ea..2098dc3c0 160000 --- a/kits/responsys-kit +++ b/kits/responsys-kit @@ -1 +1 @@ -Subproject commit 62c6e15ea46d7e37b4e7f6b0ac52ad8c20d8b5e4 +Subproject commit 2098dc3c00f73d416da666752df495a0d6a4af65 diff --git a/kits/revealmobile-kit b/kits/revealmobile-kit index bcabe5b4b..519f3c29a 160000 --- a/kits/revealmobile-kit +++ b/kits/revealmobile-kit @@ -1 +1 @@ -Subproject commit bcabe5b4bc0fc95bdd108677c17fa86f0fbf8939 +Subproject commit 519f3c29ad9fe2d31430df1c0ae691d48e0e161a diff --git a/kits/rokt-kit b/kits/rokt-kit index ff28ccdc3..de13a5105 160000 --- a/kits/rokt-kit +++ b/kits/rokt-kit @@ -1 +1 @@ -Subproject commit ff28ccdc3d513044663d9c7b407e8e5ea3ed9338 +Subproject commit de13a5105de2deefb48ef88b538ce4b635cbaa3a diff --git a/kits/singular-kit b/kits/singular-kit index 64b3434b1..5a8ab28d4 160000 --- a/kits/singular-kit +++ b/kits/singular-kit @@ -1 +1 @@ -Subproject commit 64b3434b173692283e6ac5616b1e4584deeff19e +Subproject commit 5a8ab28d40e2ae2d2682b6307386540d85a9b2fc diff --git a/kits/skyhook-kit b/kits/skyhook-kit index 19b15b725..de75c9f8b 160000 --- a/kits/skyhook-kit +++ b/kits/skyhook-kit @@ -1 +1 @@ -Subproject commit 19b15b72571ec5e9cf314427c11a8e1876ed2332 +Subproject commit de75c9f8bcc392ce504c097ecb46dd47e9864ff1 diff --git a/kits/taplytics-kit b/kits/taplytics-kit index 4654afc22..5f44e79de 160000 --- a/kits/taplytics-kit +++ b/kits/taplytics-kit @@ -1 +1 @@ -Subproject commit 4654afc22588de874367621198b67ad4db8d1cec +Subproject commit 5f44e79dee22963fdf598f645f25ba9aeefa90ae diff --git a/kits/tune-kit b/kits/tune-kit index b898766ea..0a53e6c4e 160000 --- a/kits/tune-kit +++ b/kits/tune-kit @@ -1 +1 @@ -Subproject commit b898766eae0d47a51b7db5314b0036265eae5804 +Subproject commit 0a53e6c4e1266c93743b7aa5bcef4e70ac7feb30 diff --git a/kits/urbanairship-kit b/kits/urbanairship-kit index ec958fff0..780fcbd4f 160000 --- a/kits/urbanairship-kit +++ b/kits/urbanairship-kit @@ -1 +1 @@ -Subproject commit ec958fff0ecf8602cdecf28bacb73927dd1e1181 +Subproject commit 780fcbd4f108949475954ca19c9e60378fcf6887 diff --git a/kits/wootric-kit b/kits/wootric-kit index 1c0b1efb2..108cafdb6 160000 --- a/kits/wootric-kit +++ b/kits/wootric-kit @@ -1 +1 @@ -Subproject commit 1c0b1efb2ee55ce0120b144fee5b94272e2f4324 +Subproject commit 108cafdb6314f7d6f485c7b2f5f9bc8aea7ff896 From fa5bf5fd91253ee3751acf9895af944e4a72b613 Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Wed, 27 May 2026 21:29:53 +0000 Subject: [PATCH 16/25] chore: 5.79.1 (release) ## [5.79.1](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.0...v5.79.1) (2026-05-27) ### Bug Fixes * **ci:** pin isolated-kit gradle version to prevent 6.0.0-rc.1 pull ([#714](https://github.com/mParticle/mparticle-android-sdk/issues/714)) ([eff10ea](https://github.com/mParticle/mparticle-android-sdk/commit/eff10ead69167f0ea3df248b2bbed089397600cd)) ### Updates & Maintenance * bump trunk-io/trunk-action from 1.2.4 to 1.3.1 ([#705](https://github.com/mParticle/mparticle-android-sdk/issues/705)) ([f4ceca7](https://github.com/mParticle/mparticle-android-sdk/commit/f4ceca7de2c2ba01ea87c98698ef7bb84991b4ab)) * Update submodules ([e435979](https://github.com/mParticle/mparticle-android-sdk/commit/e435979e1e966ef2b318bb29135852f9d55df55a)) --- CHANGELOG.md | 13 +++++++++++++ README.md | 6 +++--- build.gradle | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f039bb5b9..f67a79888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [5.79.1](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.0...v5.79.1) (2026-05-27) + + +### Bug Fixes + +* **ci:** pin isolated-kit gradle version to prevent 6.0.0-rc.1 pull ([#714](https://github.com/mParticle/mparticle-android-sdk/issues/714)) ([eff10ea](https://github.com/mParticle/mparticle-android-sdk/commit/eff10ead69167f0ea3df248b2bbed089397600cd)) + + +### Updates & Maintenance + +* bump trunk-io/trunk-action from 1.2.4 to 1.3.1 ([#705](https://github.com/mParticle/mparticle-android-sdk/issues/705)) ([f4ceca7](https://github.com/mParticle/mparticle-android-sdk/commit/f4ceca7de2c2ba01ea87c98698ef7bb84991b4ab)) +* Update submodules ([e435979](https://github.com/mParticle/mparticle-android-sdk/commit/e435979e1e966ef2b318bb29135852f9d55df55a)) + ## [5.79.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.5...v5.79.0) (2026-05-14) diff --git a/README.md b/README.md index 6321b413a..b24f0eb9b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can grab the Core SDK via Maven Central. Please see the badge above and foll ```groovy dependencies { - implementation 'com.mparticle:android-core:5.79.0' + implementation 'com.mparticle:android-core:5.79.1' } ``` @@ -31,8 +31,8 @@ Several integrations require additional client-side add-on libraries called "kit ```groovy dependencies { implementation ( - 'com.mparticle:android-example-kit:5.79.0', - 'com.mparticle:android-another-kit:5.79.0' + 'com.mparticle:android-example-kit:5.79.1', + 'com.mparticle:android-another-kit:5.79.1' ) } ``` diff --git a/build.gradle b/build.gradle index bef6ec38f..009d649c7 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ subprojects { allprojects { group = 'com.mparticle' - version = '5.79.0-SNAPSHOT' + version = '5.79.1-SNAPSHOT' if (project.hasProperty('isRelease') && project.isRelease) { version = version.toString().replace("-SNAPSHOT", "") } From 6cdacf2926e02e2359352dbcf12b4e4bb66236fa Mon Sep 17 00:00:00 2001 From: James Newman Date: Tue, 2 Jun 2026 10:16:33 -0400 Subject: [PATCH 17/25] ci: remove cross-platform-tests and semantic PR/branch checks (#712) These checks have been failing on nearly every recent PR. The cross-platform tests are known broken, and the semantic PR title / branch name checks are no longer enforced. Co-authored-by: Claude Opus 4.7 (1M context) --- .github/workflows/cross-platform-tests.yml | 74 ---------------------- .github/workflows/pull-request.yml | 6 -- 2 files changed, 80 deletions(-) delete mode 100644 .github/workflows/cross-platform-tests.yml diff --git a/.github/workflows/cross-platform-tests.yml b/.github/workflows/cross-platform-tests.yml deleted file mode 100644 index 0f57f8b9e..000000000 --- a/.github/workflows/cross-platform-tests.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: "Cross Platform Tests" -on: pull_request - -permissions: - contents: read - pull-requests: read -jobs: - cross-platform-tests: - timeout-minutes: 30 - runs-on: ubuntu-latest - steps: - - name: "Checkout Cross Platform Tests Repo" - uses: actions/checkout@v6.0.2 - with: - repository: mParticle/crossplatform-sdk-tests - - - name: "Checkout Android SDK Branch" - uses: actions/checkout@v6.0.2 - with: - repository: ${{github.event.pull_request.head.repo.full_name}} - ref: ${{github.head_ref}} - path: .sdks/android - - - name: Gradle cache - uses: gradle/gradle-build-action@842c587ad8aa4c68eeba24c396e15af4c2e9f30a #v2.9.0 - - - name: AVD cache - uses: actions/cache@v5 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-cache - - - name: create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b #v2.35.0 - with: - api-level: 29 - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - script: echo "Generated AVD snapshot for caching." - - name: Enable KVM group perms - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - - name: "Install JDK 17" - uses: actions/setup-java@v5 - with: - distribution: "zulu" - java-version: "17" - - name: "Run Crossplatform Tests" - uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b #v2.35.0 - with: - api-level: 29 - force-avd-creation: false - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - script: | - adb uninstall com.mparticle.kittests || true - adb uninstall com.mparticle.kits.test || true - adb uninstall com.mparticle.legacyTest || true - adb uninstall com.mparticle.test || true - adb uninstall com.mparticle.cpt.testing.test || true - ./gradlew runAndroid - - name: "Archive Test Results" - if: ${{ always() }} - uses: actions/upload-artifact@v7 - with: - name: "test-results" - path: Tests/build/reports/androidTests/connected/** diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 75160efb3..678e855be 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -27,12 +27,6 @@ jobs: pr-check-hadcoded-secrets: name: "Check PR for hardcoded secrets" uses: mParticle/mparticle-workflows/.github/workflows/security-hardcoded-secrets.yml@main - pr-branch-check-name: - name: "Check PR for semantic branch name" - uses: mParticle/mparticle-workflows/.github/workflows/pr-branch-check-name.yml@main - pr-title-check: - name: "Check PR for semantic title" - uses: mParticle/mparticle-workflows/.github/workflows/pr-title-check.yml@main instrumented-tests: name: "Instrumented Tests" From d904edb0a944a2ce813e7dc31c026f0b2f7ccecb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 15:29:57 -0400 Subject: [PATCH 18/25] chore: bump actions/checkout from 6.0.2 to 6.0.3 (#720) Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](https://github.com/actions/checkout/compare/v6.0.2...v6.0.3) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/daily.yml | 18 +++++++++--------- .github/workflows/pull-request.yml | 14 +++++++------- .github/workflows/release.yml | 8 ++++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index bbd206621..bfc47f71c 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -45,7 +45,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Clone branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: token: ${{ secrets.MP_SEMANTIC_RELEASE_BOT }} repository: mparticle/mparticle-android-sdk @@ -77,7 +77,7 @@ jobs: # needs: create-regression-branch # steps: # - name: "Checkout future release branch" - # uses: actions/checkout@v6.0.2 + # uses: actions/checkout@v6.0.3 # with: # repository: mparticle/mparticle-android-sdk # ref: regression/${{ github.run_number }} @@ -110,7 +110,7 @@ jobs: # needs: create-regression-branch # steps: # - name: "Checkout Branch" - # uses: actions/checkout@v6.0.2 + # uses: actions/checkout@v6.0.3 # with: # repository: mparticle/mparticle-android-sdk # ref: regression/${{ github.run_number }} @@ -200,7 +200,7 @@ jobs: needs: create-regression-branch steps: - name: "Checkout future release branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: repository: mparticle/mparticle-android-sdk ref: regression/${{ github.run_number }} @@ -229,7 +229,7 @@ jobs: needs: create-regression-branch steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: ref: regression/${{ github.run_number }} submodules: recursive @@ -271,7 +271,7 @@ jobs: needs: create-regression-branch steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: ref: regression/${{ github.run_number }} submodules: recursive @@ -317,7 +317,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Checkout future release branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: repository: mparticle/mparticle-android-sdk ref: regression/${{ github.run_number }} @@ -351,7 +351,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Checkout public main branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: fetch-depth: 0 ref: main @@ -382,7 +382,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Checkout repo" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: token: ${{ secrets.MP_SEMANTIC_RELEASE_BOT }} fetch-depth: 0 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 678e855be..2628f2e98 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 #v5.0.0 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v5.0.0 - name: Trunk Check uses: trunk-io/trunk-action@04ba50e7658c81db7356da96657e6e77f220bfa3 # v1.3.1 with: @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 - name: "Install JDK 17" uses: actions/setup-java@v5 with: @@ -95,7 +95,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 - name: "Install JDK 17" uses: actions/setup-java@v5 with: @@ -155,7 +155,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 - name: "Install JDK 17" uses: actions/setup-java@v5 with: @@ -179,7 +179,7 @@ jobs: runs-on: macos-latest steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: submodules: recursive - name: "Install JDK 17" @@ -202,7 +202,7 @@ jobs: runs-on: macos-latest steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: submodules: recursive - name: "Install JDK 17" @@ -231,7 +231,7 @@ jobs: if: github.event_name == 'pull_request' steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: submodules: recursive fetch-depth: 0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9676b0ee8..d4b0b4f19 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: Checkout main branch - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: token: ${{ secrets.MP_SEMANTIC_RELEASE_BOT }} repository: mparticle/mparticle-android-sdk @@ -78,7 +78,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Checkout public main branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: token: ${{ secrets.MP_SEMANTIC_RELEASE_BOT }} fetch-depth: 0 @@ -131,7 +131,7 @@ jobs: mavenSigningKeyPassword: ${{ secrets.MAVEN_CENTRAL_SIGNING_KEY_PASSWORD }} steps: - name: "Checkout release branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: repository: mparticle/mparticle-android-sdk ref: release/${{ github.run_number }} @@ -178,7 +178,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout main branch" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v6.0.3 with: fetch-depth: 0 repository: ${{ github.repository }} From 976dd0d13d6aa27f805a6b06a73d923f1a207e80 Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Mon, 22 Jun 2026 20:09:37 +0000 Subject: [PATCH 19/25] chore: Update submodules --- kits/rokt-kit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kits/rokt-kit b/kits/rokt-kit index de13a5105..0a9cd5ac8 160000 --- a/kits/rokt-kit +++ b/kits/rokt-kit @@ -1 +1 @@ -Subproject commit de13a5105de2deefb48ef88b538ce4b635cbaa3a +Subproject commit 0a9cd5ac8afaa4eee6a03f6007ca443af653877b From cbaa6235054b3a3ae83d701992ffd9cd1c18380f Mon Sep 17 00:00:00 2001 From: mparticle-automation Date: Mon, 22 Jun 2026 20:24:00 +0000 Subject: [PATCH 20/25] chore: 5.79.2 (release) ## [5.79.2](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.1...v5.79.2) (2026-06-22) ### Updates & Maintenance * bump actions/checkout from 6.0.2 to 6.0.3 ([#720](https://github.com/mParticle/mparticle-android-sdk/issues/720)) ([d904edb](https://github.com/mParticle/mparticle-android-sdk/commit/d904edb0a944a2ce813e7dc31c026f0b2f7ccecb)) * remove cross-platform-tests and semantic PR/branch checks ([#712](https://github.com/mParticle/mparticle-android-sdk/issues/712)) ([6cdacf2](https://github.com/mParticle/mparticle-android-sdk/commit/6cdacf2926e02e2359352dbcf12b4e4bb66236fa)) * Update submodules ([976dd0d](https://github.com/mParticle/mparticle-android-sdk/commit/976dd0d13d6aa27f805a6b06a73d923f1a207e80)) --- CHANGELOG.md | 9 +++++++++ README.md | 6 +++--- build.gradle | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f67a79888..2e15999b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [5.79.2](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.1...v5.79.2) (2026-06-22) + + +### Updates & Maintenance + +* bump actions/checkout from 6.0.2 to 6.0.3 ([#720](https://github.com/mParticle/mparticle-android-sdk/issues/720)) ([d904edb](https://github.com/mParticle/mparticle-android-sdk/commit/d904edb0a944a2ce813e7dc31c026f0b2f7ccecb)) +* remove cross-platform-tests and semantic PR/branch checks ([#712](https://github.com/mParticle/mparticle-android-sdk/issues/712)) ([6cdacf2](https://github.com/mParticle/mparticle-android-sdk/commit/6cdacf2926e02e2359352dbcf12b4e4bb66236fa)) +* Update submodules ([976dd0d](https://github.com/mParticle/mparticle-android-sdk/commit/976dd0d13d6aa27f805a6b06a73d923f1a207e80)) + ## [5.79.1](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.0...v5.79.1) (2026-05-27) diff --git a/README.md b/README.md index b24f0eb9b..c78eb8a9d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can grab the Core SDK via Maven Central. Please see the badge above and foll ```groovy dependencies { - implementation 'com.mparticle:android-core:5.79.1' + implementation 'com.mparticle:android-core:5.79.2' } ``` @@ -31,8 +31,8 @@ Several integrations require additional client-side add-on libraries called "kit ```groovy dependencies { implementation ( - 'com.mparticle:android-example-kit:5.79.1', - 'com.mparticle:android-another-kit:5.79.1' + 'com.mparticle:android-example-kit:5.79.2', + 'com.mparticle:android-another-kit:5.79.2' ) } ``` diff --git a/build.gradle b/build.gradle index 009d649c7..99f2034d2 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ subprojects { allprojects { group = 'com.mparticle' - version = '5.79.1-SNAPSHOT' + version = '5.79.2-SNAPSHOT' if (project.hasProperty('isRelease') && project.isRelease) { version = version.toString().replace("-SNAPSHOT", "") } From e92d3522a350be90a1f15eb3b81d5e6f089f1aed Mon Sep 17 00:00:00 2001 From: denischilik Date: Thu, 25 Jun 2026 12:20:15 -0400 Subject: [PATCH 21/25] feat: add device-based consent to override MPID-scoped consent (#726) Enables Inspire-style flows where consent is collected before MPID changes at checkout, so kit forwarding rules and uploads keep the correct consent state. --- CHANGELOG.md | 145 ++++++++---------- .../main/java/com/mparticle/MParticle.java | 43 ++++++ .../java/com/mparticle/MParticleOptions.java | 36 +++++ .../identity/MParticleUserDelegate.java | 8 +- .../com/mparticle/internal/ConfigManager.java | 48 ++++++ .../com/mparticle/internal/MessageBatch.java | 2 +- .../com/mparticle/internal/Constants.kt | 2 + .../mparticle/external/ApiVisibilityTest.kt | 2 +- .../mparticle/internal/ConfigManagerTest.kt | 36 +++++ 9 files changed, 235 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e15999b7..f706e437a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,200 +1,179 @@ -## [5.79.2](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.1...v5.79.2) (2026-06-22) +## Unreleased + +### Added +- Add device-based consent APIs (`MParticle.getDeviceConsentState()`, `MParticle.setDeviceConsentState()`) and `MParticleOptions.Builder.deviceBasedConsentEnabled()` so consent can be stored and applied at the device level, overriding MPID-based consent for kit forwarding rules and event uploads. + +## [5.79.2](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.1...v5.79.2) (2026-06-22) ### Updates & Maintenance -* bump actions/checkout from 6.0.2 to 6.0.3 ([#720](https://github.com/mParticle/mparticle-android-sdk/issues/720)) ([d904edb](https://github.com/mParticle/mparticle-android-sdk/commit/d904edb0a944a2ce813e7dc31c026f0b2f7ccecb)) -* remove cross-platform-tests and semantic PR/branch checks ([#712](https://github.com/mParticle/mparticle-android-sdk/issues/712)) ([6cdacf2](https://github.com/mParticle/mparticle-android-sdk/commit/6cdacf2926e02e2359352dbcf12b4e4bb66236fa)) -* Update submodules ([976dd0d](https://github.com/mParticle/mparticle-android-sdk/commit/976dd0d13d6aa27f805a6b06a73d923f1a207e80)) +- bump actions/checkout from 6.0.2 to 6.0.3 ([#720](https://github.com/mParticle/mparticle-android-sdk/issues/720)) ([d904edb](https://github.com/mParticle/mparticle-android-sdk/commit/d904edb0a944a2ce813e7dc31c026f0b2f7ccecb)) +- remove cross-platform-tests and semantic PR/branch checks ([#712](https://github.com/mParticle/mparticle-android-sdk/issues/712)) ([6cdacf2](https://github.com/mParticle/mparticle-android-sdk/commit/6cdacf2926e02e2359352dbcf12b4e4bb66236fa)) +- Update submodules ([976dd0d](https://github.com/mParticle/mparticle-android-sdk/commit/976dd0d13d6aa27f805a6b06a73d923f1a207e80)) ## [5.79.1](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.0...v5.79.1) (2026-05-27) - ### Bug Fixes -* **ci:** pin isolated-kit gradle version to prevent 6.0.0-rc.1 pull ([#714](https://github.com/mParticle/mparticle-android-sdk/issues/714)) ([eff10ea](https://github.com/mParticle/mparticle-android-sdk/commit/eff10ead69167f0ea3df248b2bbed089397600cd)) - +- **ci:** pin isolated-kit gradle version to prevent 6.0.0-rc.1 pull ([#714](https://github.com/mParticle/mparticle-android-sdk/issues/714)) ([eff10ea](https://github.com/mParticle/mparticle-android-sdk/commit/eff10ead69167f0ea3df248b2bbed089397600cd)) ### Updates & Maintenance -* bump trunk-io/trunk-action from 1.2.4 to 1.3.1 ([#705](https://github.com/mParticle/mparticle-android-sdk/issues/705)) ([f4ceca7](https://github.com/mParticle/mparticle-android-sdk/commit/f4ceca7de2c2ba01ea87c98698ef7bb84991b4ab)) -* Update submodules ([e435979](https://github.com/mParticle/mparticle-android-sdk/commit/e435979e1e966ef2b318bb29135852f9d55df55a)) +- bump trunk-io/trunk-action from 1.2.4 to 1.3.1 ([#705](https://github.com/mParticle/mparticle-android-sdk/issues/705)) ([f4ceca7](https://github.com/mParticle/mparticle-android-sdk/commit/f4ceca7de2c2ba01ea87c98698ef7bb84991b4ab)) +- Update submodules ([e435979](https://github.com/mParticle/mparticle-android-sdk/commit/e435979e1e966ef2b318bb29135852f9d55df55a)) ## [5.79.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.5...v5.79.0) (2026-05-14) - ### Features -* add customBaseURL CNAME support to NetworkOptions ([#701](https://github.com/mParticle/mparticle-android-sdk/issues/701)) ([5285149](https://github.com/mParticle/mparticle-android-sdk/commit/5285149118b58cbd5cf4fb90cb58627deb7538b9)), closes [mparticle-apple-sdk#760](https://github.com/mParticle/mparticle-apple-sdk/issues/760) -* Add max persistence age override option [TRIAGE-608] ([#699](https://github.com/mParticle/mparticle-android-sdk/issues/699)) ([ca88322](https://github.com/mParticle/mparticle-android-sdk/commit/ca88322ac7ef8649ed111cf28afb81d99cdb271a)) - +- add customBaseURL CNAME support to NetworkOptions ([#701](https://github.com/mParticle/mparticle-android-sdk/issues/701)) ([5285149](https://github.com/mParticle/mparticle-android-sdk/commit/5285149118b58cbd5cf4fb90cb58627deb7538b9)), closes [mparticle-apple-sdk#760](https://github.com/mParticle/mparticle-apple-sdk/issues/760) +- Add max persistence age override option [TRIAGE-608] ([#699](https://github.com/mParticle/mparticle-android-sdk/issues/699)) ([ca88322](https://github.com/mParticle/mparticle-android-sdk/commit/ca88322ac7ef8649ed111cf28afb81d99cdb271a)) ### Bug Fixes -* increase MPLatch timeout from 5s to 30s ([#695](https://github.com/mParticle/mparticle-android-sdk/issues/695)) ([20f723f](https://github.com/mParticle/mparticle-android-sdk/commit/20f723f27cf296c879ceebc9d234edca039ab371)) - +- increase MPLatch timeout from 5s to 30s ([#695](https://github.com/mParticle/mparticle-android-sdk/issues/695)) ([20f723f](https://github.com/mParticle/mparticle-android-sdk/commit/20f723f27cf296c879ceebc9d234edca039ab371)) ### Updates & Maintenance -* Update submodules ([59a4a9a](https://github.com/mParticle/mparticle-android-sdk/commit/59a4a9a68e3addfaf5c4eea1afd396889721819d)) +- Update submodules ([59a4a9a](https://github.com/mParticle/mparticle-android-sdk/commit/59a4a9a68e3addfaf5c4eea1afd396889721819d)) ## [5.78.5](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.4...v5.78.5) (2026-03-25) - ### Updates & Maintenance -* Update submodules ([86d8d97](https://github.com/mParticle/mparticle-android-sdk/commit/86d8d9748a0c7a796dd0b0be6722f92acef301ea)) +- Update submodules ([86d8d97](https://github.com/mParticle/mparticle-android-sdk/commit/86d8d9748a0c7a796dd0b0be6722f92acef301ea)) ## [5.78.4](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.3...v5.78.4) (2026-03-23) - ### Updates & Maintenance -* Update submodules ([08dab75](https://github.com/mParticle/mparticle-android-sdk/commit/08dab75e3715733f6f5655fbf39fbce16a2e01a2)) +- Update submodules ([08dab75](https://github.com/mParticle/mparticle-android-sdk/commit/08dab75e3715733f6f5655fbf39fbce16a2e01a2)) ## [5.78.3](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.2...v5.78.3) (2026-03-11) - ### Updates & Maintenance -* bump actions/upload-artifact from 6 to 7 ([#649](https://github.com/mParticle/mparticle-android-sdk/issues/649)) ([0a188b1](https://github.com/mParticle/mparticle-android-sdk/commit/0a188b1f88efde471f8d6bff2aa8611b1b4bf956)) -* Update submodules ([cbb17d7](https://github.com/mParticle/mparticle-android-sdk/commit/cbb17d7e359d41d8d7577a9516cf7788c7288442)) +- bump actions/upload-artifact from 6 to 7 ([#649](https://github.com/mParticle/mparticle-android-sdk/issues/649)) ([0a188b1](https://github.com/mParticle/mparticle-android-sdk/commit/0a188b1f88efde471f8d6bff2aa8611b1b4bf956)) +- Update submodules ([cbb17d7](https://github.com/mParticle/mparticle-android-sdk/commit/cbb17d7e359d41d8d7577a9516cf7788c7288442)) ## [5.78.2](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.1...v5.78.2) (2026-02-27) - ### Bug Fixes -* ci - use Gradle properties for SDK version extraction in release ([63b39e7](https://github.com/mParticle/mparticle-android-sdk/commit/63b39e71ec952735ac26bf10dbe4f3968120e371)) +- ci - use Gradle properties for SDK version extraction in release ([63b39e7](https://github.com/mParticle/mparticle-android-sdk/commit/63b39e71ec952735ac26bf10dbe4f3968120e371)) ## [5.78.1](https://github.com/mParticle/mparticle-android-sdk/compare/v5.78.0...v5.78.1) (2026-02-27) - ### Updates & Maintenance -* Add AGENTS file ([#646](https://github.com/mParticle/mparticle-android-sdk/issues/646)) ([8e227fd](https://github.com/mParticle/mparticle-android-sdk/commit/8e227fd256d36bdcc92cfd58ecf81806e15d3c56)) -* add isolated kit support for urbanairship-kit ([#647](https://github.com/mParticle/mparticle-android-sdk/issues/647)) ([2b24344](https://github.com/mParticle/mparticle-android-sdk/commit/2b24344cb338e9704a4cce2bc204eb32c8213492)) -* Update submodules ([0200c4c](https://github.com/mParticle/mparticle-android-sdk/commit/0200c4c5b74f17ac55663651e80f6dfae84fe12d)) +- Add AGENTS file ([#646](https://github.com/mParticle/mparticle-android-sdk/issues/646)) ([8e227fd](https://github.com/mParticle/mparticle-android-sdk/commit/8e227fd256d36bdcc92cfd58ecf81806e15d3c56)) +- add isolated kit support for urbanairship-kit ([#647](https://github.com/mParticle/mparticle-android-sdk/issues/647)) ([2b24344](https://github.com/mParticle/mparticle-android-sdk/commit/2b24344cb338e9704a4cce2bc204eb32c8213492)) +- Update submodules ([0200c4c](https://github.com/mParticle/mparticle-android-sdk/commit/0200c4c5b74f17ac55663651e80f6dfae84fe12d)) ## [5.78.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.77.0...v5.78.0) (2026-02-12) - ### Features -* Add PlacementOptions support to Rokt integration ([#639](https://github.com/mParticle/mparticle-android-sdk/issues/639)) ([61cc472](https://github.com/mParticle/mparticle-android-sdk/commit/61cc472fc42c0871380345cc74ba57a4d949310c)) - +- Add PlacementOptions support to Rokt integration ([#639](https://github.com/mParticle/mparticle-android-sdk/issues/639)) ([61cc472](https://github.com/mParticle/mparticle-android-sdk/commit/61cc472fc42c0871380345cc74ba57a4d949310c)) ### Updates & Maintenance -* Update submodules ([91191f6](https://github.com/mParticle/mparticle-android-sdk/commit/91191f64db292fe6a58de2c8ba1cd786e0e0a509)) +- Update submodules ([91191f6](https://github.com/mParticle/mparticle-android-sdk/commit/91191f64db292fe6a58de2c8ba1cd786e0e0a509)) ## [5.77.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.76.1...v5.77.0) (2026-02-03) - ### Features -* Add support for Get / Set SessionId on Rokt ([#645](https://github.com/mParticle/mparticle-android-sdk/issues/645)) ([2956a05](https://github.com/mParticle/mparticle-android-sdk/commit/2956a0547f93914e89e34c70f22820f0a3ea26ba)) - +- Add support for Get / Set SessionId on Rokt ([#645](https://github.com/mParticle/mparticle-android-sdk/issues/645)) ([2956a05](https://github.com/mParticle/mparticle-android-sdk/commit/2956a0547f93914e89e34c70f22820f0a3ea26ba)) ### Bug Fixes -* Validate js-sdk-file input for Gradle 8 compatibility ([#644](https://github.com/mParticle/mparticle-android-sdk/issues/644)) ([2725387](https://github.com/mParticle/mparticle-android-sdk/commit/2725387df98c77dff9bf251fb528fc8241f4409c)) - +- Validate js-sdk-file input for Gradle 8 compatibility ([#644](https://github.com/mParticle/mparticle-android-sdk/issues/644)) ([2725387](https://github.com/mParticle/mparticle-android-sdk/commit/2725387df98c77dff9bf251fb528fc8241f4409c)) ### Updates & Maintenance -* Update submodules ([d5d503f](https://github.com/mParticle/mparticle-android-sdk/commit/d5d503fe8443222777d08bbe667696402753a8b8)) +- Update submodules ([d5d503f](https://github.com/mParticle/mparticle-android-sdk/commit/d5d503fe8443222777d08bbe667696402753a8b8)) ## [5.76.1](https://github.com/mParticle/mparticle-android-sdk/compare/v5.76.0...v5.76.1) (2026-01-29) - ### Updates & Maintenance -* bump actions/checkout from 6.0.1 to 6.0.2 ([#641](https://github.com/mParticle/mparticle-android-sdk/issues/641)) ([227b421](https://github.com/mParticle/mparticle-android-sdk/commit/227b4218fef761a82fe10ca0e0fdd5668b0f4979)) -* Update submodules ([e6dbcb8](https://github.com/mParticle/mparticle-android-sdk/commit/e6dbcb8f57d491d1e43457dca2f44b192ad5be29)) +- bump actions/checkout from 6.0.1 to 6.0.2 ([#641](https://github.com/mParticle/mparticle-android-sdk/issues/641)) ([227b421](https://github.com/mParticle/mparticle-android-sdk/commit/227b4218fef761a82fe10ca0e0fdd5668b0f4979)) +- Update submodules ([e6dbcb8](https://github.com/mParticle/mparticle-android-sdk/commit/e6dbcb8f57d491d1e43457dca2f44b192ad5be29)) ## [5.76.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.75.1...v5.76.0) (2026-01-27) - ### Features -* Add trunk rule to detect mParticle API keys ([#633](https://github.com/mParticle/mparticle-android-sdk/issues/633)) ([3f3c79f](https://github.com/mParticle/mparticle-android-sdk/commit/3f3c79faf8b6ee8bff407428c8f11839e75faa2e)) -* Update Android Gradle plugin to 8.1.0 ([#575](https://github.com/mParticle/mparticle-android-sdk/issues/575)) ([fc6a25d](https://github.com/mParticle/mparticle-android-sdk/commit/fc6a25d354d180fc2e7a59e2a80b57880c608891)) - +- Add trunk rule to detect mParticle API keys ([#633](https://github.com/mParticle/mparticle-android-sdk/issues/633)) ([3f3c79f](https://github.com/mParticle/mparticle-android-sdk/commit/3f3c79faf8b6ee8bff407428c8f11839e75faa2e)) +- Update Android Gradle plugin to 8.1.0 ([#575](https://github.com/mParticle/mparticle-android-sdk/issues/575)) ([fc6a25d](https://github.com/mParticle/mparticle-android-sdk/commit/fc6a25d354d180fc2e7a59e2a80b57880c608891)) ### Bug Fixes -* Align ktlint rules for kits with Android Studio defaults ([#640](https://github.com/mParticle/mparticle-android-sdk/issues/640)) ([e5484a0](https://github.com/mParticle/mparticle-android-sdk/commit/e5484a0fcafed11852c580c6f3cc5e27db0934fb)) -* migrate all generic logs to Logger class ([#630](https://github.com/mParticle/mparticle-android-sdk/issues/630)) ([9c9cb33](https://github.com/mParticle/mparticle-android-sdk/commit/9c9cb338424bf2f95038ed2456014f12bb7fe9b3)) -* Override ktlint rules for kits to avoid editorconfig conflicts ([#642](https://github.com/mParticle/mparticle-android-sdk/issues/642)) ([e31027a](https://github.com/mParticle/mparticle-android-sdk/commit/e31027a9fe70f483fd188a97641aa53fffeddbb3)) - +- Align ktlint rules for kits with Android Studio defaults ([#640](https://github.com/mParticle/mparticle-android-sdk/issues/640)) ([e5484a0](https://github.com/mParticle/mparticle-android-sdk/commit/e5484a0fcafed11852c580c6f3cc5e27db0934fb)) +- migrate all generic logs to Logger class ([#630](https://github.com/mParticle/mparticle-android-sdk/issues/630)) ([9c9cb33](https://github.com/mParticle/mparticle-android-sdk/commit/9c9cb338424bf2f95038ed2456014f12bb7fe9b3)) +- Override ktlint rules for kits to avoid editorconfig conflicts ([#642](https://github.com/mParticle/mparticle-android-sdk/issues/642)) ([e31027a](https://github.com/mParticle/mparticle-android-sdk/commit/e31027a9fe70f483fd188a97641aa53fffeddbb3)) ### Updates & Maintenance -* bump actions/cache from 4 to 5 ([#635](https://github.com/mParticle/mparticle-android-sdk/issues/635)) ([fb471fa](https://github.com/mParticle/mparticle-android-sdk/commit/fb471fa5ffec283a03fd629ee2758262a726582e)) -* bump actions/checkout from 5 to 6 ([#631](https://github.com/mParticle/mparticle-android-sdk/issues/631)) ([a0b2442](https://github.com/mParticle/mparticle-android-sdk/commit/a0b24424aaa43808a4fdac028d82cc4db8d40262)) -* bump actions/checkout from 5.0.0 to 6.0.1 ([#637](https://github.com/mParticle/mparticle-android-sdk/issues/637)) ([449a0b5](https://github.com/mParticle/mparticle-android-sdk/commit/449a0b5c22293bfe21336002cf2835eb81c8a1af)) -* bump actions/setup-java from 4 to 5 ([#625](https://github.com/mParticle/mparticle-android-sdk/issues/625)) ([46823b8](https://github.com/mParticle/mparticle-android-sdk/commit/46823b8d1174bdb5345483ab43407bbe82e34f88)) -* bump actions/upload-artifact from 5 to 6 ([#636](https://github.com/mParticle/mparticle-android-sdk/issues/636)) ([05f6133](https://github.com/mParticle/mparticle-android-sdk/commit/05f6133f5e69f1bc1d331fd4772e8f4134fec539)) -* Remove empty workflow file ([#634](https://github.com/mParticle/mparticle-android-sdk/issues/634)) ([0780abd](https://github.com/mParticle/mparticle-android-sdk/commit/0780abd0387a4b8281e0c8432180813719f080ad)) -* Update submodules ([d6bf37f](https://github.com/mParticle/mparticle-android-sdk/commit/d6bf37f7ad63f467efeb7c706c40841be48fbc5b)) +- bump actions/cache from 4 to 5 ([#635](https://github.com/mParticle/mparticle-android-sdk/issues/635)) ([fb471fa](https://github.com/mParticle/mparticle-android-sdk/commit/fb471fa5ffec283a03fd629ee2758262a726582e)) +- bump actions/checkout from 5 to 6 ([#631](https://github.com/mParticle/mparticle-android-sdk/issues/631)) ([a0b2442](https://github.com/mParticle/mparticle-android-sdk/commit/a0b24424aaa43808a4fdac028d82cc4db8d40262)) +- bump actions/checkout from 5.0.0 to 6.0.1 ([#637](https://github.com/mParticle/mparticle-android-sdk/issues/637)) ([449a0b5](https://github.com/mParticle/mparticle-android-sdk/commit/449a0b5c22293bfe21336002cf2835eb81c8a1af)) +- bump actions/setup-java from 4 to 5 ([#625](https://github.com/mParticle/mparticle-android-sdk/issues/625)) ([46823b8](https://github.com/mParticle/mparticle-android-sdk/commit/46823b8d1174bdb5345483ab43407bbe82e34f88)) +- bump actions/upload-artifact from 5 to 6 ([#636](https://github.com/mParticle/mparticle-android-sdk/issues/636)) ([05f6133](https://github.com/mParticle/mparticle-android-sdk/commit/05f6133f5e69f1bc1d331fd4772e8f4134fec539)) +- Remove empty workflow file ([#634](https://github.com/mParticle/mparticle-android-sdk/issues/634)) ([0780abd](https://github.com/mParticle/mparticle-android-sdk/commit/0780abd0387a4b8281e0c8432180813719f080ad)) +- Update submodules ([d6bf37f](https://github.com/mParticle/mparticle-android-sdk/commit/d6bf37f7ad63f467efeb7c706c40841be48fbc5b)) ## [5.75.1](https://github.com/mParticle/mparticle-android-sdk/compare/v5.75.0...v5.75.1) (2025-11-20) - ### Updates & Maintenance -* restore rokt-kit submodule ([#629](https://github.com/mParticle/mparticle-android-sdk/issues/629)) ([6bd0fba](https://github.com/mParticle/mparticle-android-sdk/commit/6bd0fbae72d1f587deb32fdda60612c0f90c6540)) +- restore rokt-kit submodule ([#629](https://github.com/mParticle/mparticle-android-sdk/issues/629)) ([6bd0fba](https://github.com/mParticle/mparticle-android-sdk/commit/6bd0fbae72d1f587deb32fdda60612c0f90c6540)) ## [5.75.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.74.3...v5.75.0) (2025-11-20) - ### Features -* Implement Trunk tool ([#604](https://github.com/mParticle/mparticle-android-sdk/issues/604)) ([d02a125](https://github.com/mParticle/mparticle-android-sdk/commit/d02a1252ef7d08513cbdb8e53cd759755be2ef5d)) - +- Implement Trunk tool ([#604](https://github.com/mParticle/mparticle-android-sdk/issues/604)) ([d02a125](https://github.com/mParticle/mparticle-android-sdk/commit/d02a1252ef7d08513cbdb8e53cd759755be2ef5d)) ### Updates & Maintenance -* add token to checkout step for regression branch pushes ([#627](https://github.com/mParticle/mparticle-android-sdk/issues/627)) ([afe965e](https://github.com/mParticle/mparticle-android-sdk/commit/afe965edd8ec1eda5ca1864e0df0dde535092567)) -* bump andymckay/cancel-action from 435124153eb37d6a62a29d053a7e449652f89d51 to a955d435292c0d409d104b57d8e78435a93a6ef1 ([#624](https://github.com/mParticle/mparticle-android-sdk/issues/624)) ([2f1644a](https://github.com/mParticle/mparticle-android-sdk/commit/2f1644a5a88053f60cb5696ff471fcfd9b1a4321)) -* bump reactivecircus/android-emulator-runner from 2.34.0 to 2.35.0 ([#626](https://github.com/mParticle/mparticle-android-sdk/issues/626)) ([8f4b392](https://github.com/mParticle/mparticle-android-sdk/commit/8f4b3927b63cd8dd80e6ae703f50ad49982de634)) -* update permissions to allow branch push and delete operations ([#628](https://github.com/mParticle/mparticle-android-sdk/issues/628)) ([d66079d](https://github.com/mParticle/mparticle-android-sdk/commit/d66079dc78ad8dbe8ab09415bf9a5af43528b4aa)) +- add token to checkout step for regression branch pushes ([#627](https://github.com/mParticle/mparticle-android-sdk/issues/627)) ([afe965e](https://github.com/mParticle/mparticle-android-sdk/commit/afe965edd8ec1eda5ca1864e0df0dde535092567)) +- bump andymckay/cancel-action from 435124153eb37d6a62a29d053a7e449652f89d51 to a955d435292c0d409d104b57d8e78435a93a6ef1 ([#624](https://github.com/mParticle/mparticle-android-sdk/issues/624)) ([2f1644a](https://github.com/mParticle/mparticle-android-sdk/commit/2f1644a5a88053f60cb5696ff471fcfd9b1a4321)) +- bump reactivecircus/android-emulator-runner from 2.34.0 to 2.35.0 ([#626](https://github.com/mParticle/mparticle-android-sdk/issues/626)) ([8f4b392](https://github.com/mParticle/mparticle-android-sdk/commit/8f4b3927b63cd8dd80e6ae703f50ad49982de634)) +- update permissions to allow branch push and delete operations ([#628](https://github.com/mParticle/mparticle-android-sdk/issues/628)) ([d66079d](https://github.com/mParticle/mparticle-android-sdk/commit/d66079dc78ad8dbe8ab09415bf9a5af43528b4aa)) ## [5.74.3](https://github.com/mParticle/mparticle-android-sdk/compare/v5.74.2...v5.74.3) (2025-11-04) - ### Updates & Maintenance -* Add step to ensure deployment visibility ([#611](https://github.com/mParticle/mparticle-android-sdk/issues/611)) ([9b5af9e](https://github.com/mParticle/mparticle-android-sdk/commit/9b5af9e2ddc761dfe8552d5801fe1bac38df2a8e)) -* bump actions/cache from 3 to 4 ([#622](https://github.com/mParticle/mparticle-android-sdk/issues/622)) ([7c46e93](https://github.com/mParticle/mparticle-android-sdk/commit/7c46e9320bc94f1ce72c9786fdcca0ddd89dd661)) -* bump actions/checkout from 3 to 5 ([#617](https://github.com/mParticle/mparticle-android-sdk/issues/617)) ([59fd344](https://github.com/mParticle/mparticle-android-sdk/commit/59fd34423643f25aea5b0e5aac35256d789e7c86)) -* bump actions/upload-artifact from 4 to 5 ([#614](https://github.com/mParticle/mparticle-android-sdk/issues/614)) ([d4f7882](https://github.com/mParticle/mparticle-android-sdk/commit/d4f788229a363fd150a9f43895d8e32425db0f64)) -* bump crazy-max/ghaction-import-gpg from 6.0.0 to 6.3.0 ([#615](https://github.com/mParticle/mparticle-android-sdk/issues/615)) ([8881009](https://github.com/mParticle/mparticle-android-sdk/commit/88810095bd87f945d8476c66999b54aead05c758)) -* bump reactivecircus/android-emulator-runner from 2.33.0 to 2.34.0 ([#623](https://github.com/mParticle/mparticle-android-sdk/issues/623)) ([15a16a8](https://github.com/mParticle/mparticle-android-sdk/commit/15a16a8275f29bb0f4e7a6239bed5d88d7c969f5)) -* Enable Dependabot for Github Actions ([#608](https://github.com/mParticle/mparticle-android-sdk/issues/608)) ([0174ff2](https://github.com/mParticle/mparticle-android-sdk/commit/0174ff2483636bd519f2f597aefc2d19f4db9d5c)) -* Remove target branch check ([#610](https://github.com/mParticle/mparticle-android-sdk/issues/610)) ([ac69641](https://github.com/mParticle/mparticle-android-sdk/commit/ac696417774da8c1f30593bc8c1b5d0e01f0c18a)) -* Update submodules ([ec57498](https://github.com/mParticle/mparticle-android-sdk/commit/ec57498a5fc007a2f99f3183046093452d8d461e)) +- Add step to ensure deployment visibility ([#611](https://github.com/mParticle/mparticle-android-sdk/issues/611)) ([9b5af9e](https://github.com/mParticle/mparticle-android-sdk/commit/9b5af9e2ddc761dfe8552d5801fe1bac38df2a8e)) +- bump actions/cache from 3 to 4 ([#622](https://github.com/mParticle/mparticle-android-sdk/issues/622)) ([7c46e93](https://github.com/mParticle/mparticle-android-sdk/commit/7c46e9320bc94f1ce72c9786fdcca0ddd89dd661)) +- bump actions/checkout from 3 to 5 ([#617](https://github.com/mParticle/mparticle-android-sdk/issues/617)) ([59fd344](https://github.com/mParticle/mparticle-android-sdk/commit/59fd34423643f25aea5b0e5aac35256d789e7c86)) +- bump actions/upload-artifact from 4 to 5 ([#614](https://github.com/mParticle/mparticle-android-sdk/issues/614)) ([d4f7882](https://github.com/mParticle/mparticle-android-sdk/commit/d4f788229a363fd150a9f43895d8e32425db0f64)) +- bump crazy-max/ghaction-import-gpg from 6.0.0 to 6.3.0 ([#615](https://github.com/mParticle/mparticle-android-sdk/issues/615)) ([8881009](https://github.com/mParticle/mparticle-android-sdk/commit/88810095bd87f945d8476c66999b54aead05c758)) +- bump reactivecircus/android-emulator-runner from 2.33.0 to 2.34.0 ([#623](https://github.com/mParticle/mparticle-android-sdk/issues/623)) ([15a16a8](https://github.com/mParticle/mparticle-android-sdk/commit/15a16a8275f29bb0f4e7a6239bed5d88d7c969f5)) +- Enable Dependabot for Github Actions ([#608](https://github.com/mParticle/mparticle-android-sdk/issues/608)) ([0174ff2](https://github.com/mParticle/mparticle-android-sdk/commit/0174ff2483636bd519f2f597aefc2d19f4db9d5c)) +- Remove target branch check ([#610](https://github.com/mParticle/mparticle-android-sdk/issues/610)) ([ac69641](https://github.com/mParticle/mparticle-android-sdk/commit/ac696417774da8c1f30593bc8c1b5d0e01f0c18a)) +- Update submodules ([ec57498](https://github.com/mParticle/mparticle-android-sdk/commit/ec57498a5fc007a2f99f3183046093452d8d461e)) ## [5.74.2](https://github.com/mParticle/mparticle-android-sdk/compare/v5.74.1...v5.74.2) (2025-10-28) - ### Updates & Maintenance -* Update submodules ([c0b4106](https://github.com/mParticle/mparticle-android-sdk/commit/c0b4106618780ee0e80bff12ce9a042c19da81dc)) +- Update submodules ([c0b4106](https://github.com/mParticle/mparticle-android-sdk/commit/c0b4106618780ee0e80bff12ce9a042c19da81dc)) ## [5.74.1](https://github.com/mParticle/mparticle-android-sdk/compare/v5.74.0...v5.74.1) (2025-10-21) - ### Bug Fixes -* Add proguard rules to prevent stripping of Rokt class ([#607](https://github.com/mParticle/mparticle-android-sdk/issues/607)) ([18ef17b](https://github.com/mParticle/mparticle-android-sdk/commit/18ef17bdf23e388f736814b5d9f579af3258229c)) - +- Add proguard rules to prevent stripping of Rokt class ([#607](https://github.com/mParticle/mparticle-android-sdk/issues/607)) ([18ef17b](https://github.com/mParticle/mparticle-android-sdk/commit/18ef17bdf23e388f736814b5d9f579af3258229c)) ### Updates & Maintenance -* Change release to use main branch ([#609](https://github.com/mParticle/mparticle-android-sdk/issues/609)) ([7d41cc3](https://github.com/mParticle/mparticle-android-sdk/commit/7d41cc3230ea4df8f6d4b639414e5afa66705828)) -* Update submodules ([e233046](https://github.com/mParticle/mparticle-android-sdk/commit/e233046a09d4de845d4cf4a7bf75d03dcb12aa3b)) +- Change release to use main branch ([#609](https://github.com/mParticle/mparticle-android-sdk/issues/609)) ([7d41cc3](https://github.com/mParticle/mparticle-android-sdk/commit/7d41cc3230ea4df8f6d4b639414e5afa66705828)) +- Update submodules ([e233046](https://github.com/mParticle/mparticle-android-sdk/commit/e233046a09d4de845d4cf4a7bf75d03dcb12aa3b)) ## [5.74.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.73.2...v5.74.0) (2025-09-23) diff --git a/android-core/src/main/java/com/mparticle/MParticle.java b/android-core/src/main/java/com/mparticle/MParticle.java index 26d07c2d4..a8c2f955f 100644 --- a/android-core/src/main/java/com/mparticle/MParticle.java +++ b/android-core/src/main/java/com/mparticle/MParticle.java @@ -20,6 +20,7 @@ import androidx.annotation.RequiresApi; import com.mparticle.commerce.CommerceEvent; +import com.mparticle.consent.ConsentState; import com.mparticle.identity.IdentityApi; import com.mparticle.identity.IdentityApiRequest; import com.mparticle.identity.IdentityApiResult; @@ -911,6 +912,48 @@ public void setOptOut(@NonNull Boolean optOutStatus) { } } + /** + * Query the device-level consent state. + *

+ * Device-level consent, when set, overrides MPID-based consent when applying consent forwarding + * rules and uploading events. + * + * @return the device-level consent state, or an empty state if none has been set + */ + @NonNull + public ConsentState getDeviceConsentState() { + return mConfigManager.getDeviceConsentState(); + } + + /** + * Set the device-level consent state. + *

+ * Device-level consent overrides MPID-based consent when applying consent forwarding rules + * and uploading events. Pass {@code null} to clear the device-level override and fall back to + * MPID-based consent. + * + * @param state the device-level consent state, or {@code null} to clear the override + */ + public void setDeviceConsentState(@Nullable ConsentState state) { + ConsentState oldState = mConfigManager.getEffectiveConsentState(mConfigManager.getMpid()); + mConfigManager.setDeviceConsentState(state); + ConsentState newState = mConfigManager.getEffectiveConsentState(mConfigManager.getMpid()); + mKitManager.onConsentStateUpdated(oldState, newState, mConfigManager.getMpid()); + } + + /** + * Query whether device-based consent is enabled. + *

+ * When enabled, {@link com.mparticle.identity.MParticleUser#setConsentState(ConsentState)} + * also persists consent at the device level. + * + * @return true if device-based consent is enabled + */ + @NonNull + public Boolean isDeviceBasedConsentEnabled() { + return mConfigManager.isDeviceBasedConsentEnabled(); + } + /** * Retrieve a URL to be loaded within a {@link WebView} to show the user a survey * or feedback form. diff --git a/android-core/src/main/java/com/mparticle/MParticleOptions.java b/android-core/src/main/java/com/mparticle/MParticleOptions.java index 3b4002224..2e068d8d2 100644 --- a/android-core/src/main/java/com/mparticle/MParticleOptions.java +++ b/android-core/src/main/java/com/mparticle/MParticleOptions.java @@ -38,6 +38,7 @@ public class MParticleOptions { private String mApiSecret; private IdentityApiRequest mIdentifyRequest; private Boolean mDevicePerformanceMetricsDisabled = false; + private Boolean mDeviceBasedConsentEnabled = false; private Boolean mAndroidIdEnabled = false; private Integer mUploadInterval = ConfigManager.DEFAULT_UPLOAD_INTERVAL; //seconds private Integer mSessionTimeout = ConfigManager.DEFAULT_SESSION_TIMEOUT_SECONDS; //seconds @@ -93,6 +94,9 @@ public MParticleOptions(@NonNull Builder builder) { if (builder.devicePerformanceMetricsDisabled != null) { this.mDevicePerformanceMetricsDisabled = builder.devicePerformanceMetricsDisabled; } + if (builder.deviceBasedConsentEnabled != null) { + this.mDeviceBasedConsentEnabled = builder.deviceBasedConsentEnabled; + } if (builder.androidIdEnabled != null) { this.mAndroidIdEnabled = builder.androidIdEnabled; } @@ -254,6 +258,20 @@ public Boolean isDevicePerformanceMetricsDisabled() { return mDevicePerformanceMetricsDisabled; } + /** + * Query whether device-based consent is enabled. + *

+ * When enabled, {@link com.mparticle.identity.MParticleUser#setConsentState(ConsentState)} + * will persist consent at the device level in addition to the current MPID. Device-level + * consent overrides MPID-based consent when applying consent forwarding rules and uploading events. + * + * @return true if device-based consent is enabled + */ + @NonNull + public Boolean isDeviceBasedConsentEnabled() { + return mDeviceBasedConsentEnabled; + } + /** * @return true if collection is disabled, false if it is enabled * @deprecated This method has been replaced as the behavior has been inverted - Android ID collection is now disabled by default. @@ -423,6 +441,7 @@ public static class Builder { private MParticle.Environment environment; private IdentityApiRequest identifyRequest; private Boolean devicePerformanceMetricsDisabled = null; + private Boolean deviceBasedConsentEnabled = null; private Boolean androidIdEnabled = null; private Integer uploadInterval = null; private Integer sessionTimeout = null; @@ -570,6 +589,23 @@ public Builder devicePerformanceMetricsDisabled(boolean disabled) { return this; } + /** + * Enable device-based consent. + *

+ * When enabled, consent set via {@link com.mparticle.identity.MParticleUser#setConsentState(ConsentState)} + * is stored at the device level and overrides MPID-based consent when applying consent forwarding + * rules and uploading events. This is useful when consent is collected before the user's MPID is known + * or when the MPID changes during a flow such as checkout. + * + * @param enabled true to enable device-based consent + * @return the instance of the builder, for chaining calls + */ + @NonNull + public Builder deviceBasedConsentEnabled(boolean enabled) { + this.deviceBasedConsentEnabled = enabled; + return this; + } + /** * @param disabled false to enable collection (true by default) * @return the instance of the builder, for chaining calls diff --git a/android-core/src/main/java/com/mparticle/identity/MParticleUserDelegate.java b/android-core/src/main/java/com/mparticle/identity/MParticleUserDelegate.java index 4f78591c5..f3d98a1e9 100644 --- a/android-core/src/main/java/com/mparticle/identity/MParticleUserDelegate.java +++ b/android-core/src/main/java/com/mparticle/identity/MParticleUserDelegate.java @@ -276,13 +276,17 @@ boolean setUser(Context context, long previousMpid, long newMpid, Map Date: Thu, 25 Jun 2026 17:10:46 +0000 Subject: [PATCH 22/25] chore: 5.80.0 (release) ## [5.80.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.2...v5.80.0) (2026-06-25) ### Features * add device-based consent to override MPID-scoped consent ([#726](https://github.com/mParticle/mparticle-android-sdk/issues/726)) ([e92d352](https://github.com/mParticle/mparticle-android-sdk/commit/e92d3522a350be90a1f15eb3b81d5e6f089f1aed)) --- CHANGELOG.md | 7 +++++++ README.md | 6 +++--- build.gradle | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f706e437a..27eb72c50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [5.80.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.2...v5.80.0) (2026-06-25) + + +### Features + +* add device-based consent to override MPID-scoped consent ([#726](https://github.com/mParticle/mparticle-android-sdk/issues/726)) ([e92d352](https://github.com/mParticle/mparticle-android-sdk/commit/e92d3522a350be90a1f15eb3b81d5e6f089f1aed)) + ## Unreleased ### Added diff --git a/README.md b/README.md index c78eb8a9d..f6be482de 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can grab the Core SDK via Maven Central. Please see the badge above and foll ```groovy dependencies { - implementation 'com.mparticle:android-core:5.79.2' + implementation 'com.mparticle:android-core:5.80.0' } ``` @@ -31,8 +31,8 @@ Several integrations require additional client-side add-on libraries called "kit ```groovy dependencies { implementation ( - 'com.mparticle:android-example-kit:5.79.2', - 'com.mparticle:android-another-kit:5.79.2' + 'com.mparticle:android-example-kit:5.80.0', + 'com.mparticle:android-another-kit:5.80.0' ) } ``` diff --git a/build.gradle b/build.gradle index 99f2034d2..f4722977d 100644 --- a/build.gradle +++ b/build.gradle @@ -63,7 +63,7 @@ subprojects { allprojects { group = 'com.mparticle' - version = '5.79.2-SNAPSHOT' + version = '5.80.0-SNAPSHOT' if (project.hasProperty('isRelease') && project.isRelease) { version = version.toString().replace("-SNAPSHOT", "") } From 0b5056faad6ddaaa5440db5de8a8c45341d52c2d Mon Sep 17 00:00:00 2001 From: Denis Chilik Date: Thu, 16 Jul 2026 14:09:21 -0400 Subject: [PATCH 23/25] chore: normalize CHANGELOG list markers after merge --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac93e1a56..50f83ad9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,10 +122,9 @@ ## [5.80.0](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.2...v5.80.0) (2026-06-25) - ### Features -* add device-based consent to override MPID-scoped consent ([#726](https://github.com/mParticle/mparticle-android-sdk/issues/726)) ([e92d352](https://github.com/mParticle/mparticle-android-sdk/commit/e92d3522a350be90a1f15eb3b81d5e6f089f1aed)) +- add device-based consent to override MPID-scoped consent ([#726](https://github.com/mParticle/mparticle-android-sdk/issues/726)) ([e92d352](https://github.com/mParticle/mparticle-android-sdk/commit/e92d3522a350be90a1f15eb3b81d5e6f089f1aed)) ## Unreleased From 5d88d1576b2c9fc80b2122b3cbc487fda5a8a8fe Mon Sep 17 00:00:00 2001 From: Denis Chilik Date: Thu, 16 Jul 2026 14:14:01 -0400 Subject: [PATCH 24/25] ci: pin actions/checkout to commit SHA to satisfy security lint --- .github/workflows/daily.yml | 18 +++++++++--------- .github/workflows/pull-request.yml | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index 2fb026152..eb7d41c8b 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -45,7 +45,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Clone branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: token: ${{ secrets.MP_SEMANTIC_RELEASE_BOT }} repository: mparticle/mparticle-android-sdk @@ -69,7 +69,7 @@ jobs: # needs: create-regression-branch # steps: # - name: "Checkout future release branch" - # uses: actions/checkout@v6.0.3 + # uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # with: # repository: mparticle/mparticle-android-sdk # ref: regression/${{ github.run_number }} @@ -102,7 +102,7 @@ jobs: # needs: create-regression-branch # steps: # - name: "Checkout Branch" - # uses: actions/checkout@v6.0.3 + # uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # with: # repository: mparticle/mparticle-android-sdk # ref: regression/${{ github.run_number }} @@ -192,7 +192,7 @@ jobs: needs: create-regression-branch steps: - name: "Checkout future release branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: repository: mparticle/mparticle-android-sdk ref: regression/${{ github.run_number }} @@ -221,7 +221,7 @@ jobs: needs: create-regression-branch steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: regression/${{ github.run_number }} - run: echo "ORG_GRADLE_PROJECT_VERSION=$(head -n 1 VERSION)" >> $GITHUB_ENV @@ -264,7 +264,7 @@ jobs: needs: create-regression-branch steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: regression/${{ github.run_number }} - run: echo "ORG_GRADLE_PROJECT_VERSION=$(head -n 1 VERSION)" >> $GITHUB_ENV @@ -311,7 +311,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Checkout future release branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: repository: mparticle/mparticle-android-sdk ref: regression/${{ github.run_number }} @@ -346,7 +346,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Checkout public main branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 ref: main @@ -377,7 +377,7 @@ jobs: GIT_COMMITTER_EMAIL: developers@mparticle.com steps: - name: "Checkout repo" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: token: ${{ secrets.MP_SEMANTIC_RELEASE_BOT }} fetch-depth: 0 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 755d5b0b1..4a97ad0ea 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -69,7 +69,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: "Install JDK 17" uses: actions/setup-java@v5 with: @@ -93,7 +93,7 @@ jobs: runs-on: macos-latest steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - run: echo "ORG_GRADLE_PROJECT_VERSION=$(head -n 1 VERSION)" >> $GITHUB_ENV - run: echo "ORG_GRADLE_PROJECT_version=$(head -n 1 VERSION)" >> $GITHUB_ENV - name: "Install JDK 17" @@ -120,7 +120,7 @@ jobs: runs-on: macos-latest steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - run: echo "ORG_GRADLE_PROJECT_VERSION=$(head -n 1 VERSION)" >> $GITHUB_ENV - run: echo "ORG_GRADLE_PROJECT_version=$(head -n 1 VERSION)" >> $GITHUB_ENV - name: "Install JDK 17" @@ -156,7 +156,7 @@ jobs: if: github.event_name == 'pull_request' steps: - name: "Checkout Branch" - uses: actions/checkout@v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - run: echo "ORG_GRADLE_PROJECT_VERSION=$(head -n 1 VERSION)" >> $GITHUB_ENV - run: echo "ORG_GRADLE_PROJECT_version=$(head -n 1 VERSION)" >> $GITHUB_ENV - name: "Install JDK 17" From 0c13ba195d3bb80496f29d7203bc596179df025c Mon Sep 17 00:00:00 2001 From: Denis Chilik Date: Thu, 16 Jul 2026 16:02:36 -0400 Subject: [PATCH 25/25] docs: remove duplicate Unreleased changelog section --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50f83ad9d..75756d319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,12 +126,6 @@ - add device-based consent to override MPID-scoped consent ([#726](https://github.com/mParticle/mparticle-android-sdk/issues/726)) ([e92d352](https://github.com/mParticle/mparticle-android-sdk/commit/e92d3522a350be90a1f15eb3b81d5e6f089f1aed)) -## Unreleased - -### Added - -- Add device-based consent APIs (`MParticle.getDeviceConsentState()`, `MParticle.setDeviceConsentState()`) and `MParticleOptions.Builder.deviceBasedConsentEnabled()` so consent can be stored and applied at the device level, overriding MPID-based consent for kit forwarding rules and event uploads. - ## [5.79.2](https://github.com/mParticle/mparticle-android-sdk/compare/v5.79.1...v5.79.2) (2026-06-22) ### Updates & Maintenance