Skip to content

Prevent duplicate subscription IDs from crashing Compose lists - #6172

Merged
2dust merged 1 commit into
2dust:masterfrom
eliotcougar:codex/fix-subscription-index-duplicates
Sep 5, 2026
Merged

2dust merged 1 commit into
2dust:masterfrom
eliotcougar:codex/fix-subscription-index-duplicates

Conversation

@eliotcougar

Copy link
Copy Markdown
Contributor

Summary

Prevent duplicate entries in the persisted subscription index from producing duplicate Compose keys. The production change is one distinct() call in MmkvManager.decodeSubsList(), with a short explanatory comment and six regression tests.

Why

Subscription records are stored by GUID, while their display order is stored separately in the MAIN/SUB_IDS JSON array. If that array contains repeated GUIDs, decodeSubscriptions() currently emits the same record repeatedly. Both the subscription LazyColumn and its reorderable items use that GUID as their key, so a damaged index can cause a duplicate-key Compose crash.

For example, with existing records a, b, and c:

Saved index:       [b, a, b, c, a]
Before:            [b, a, b, c, a]
After:             [b, a, c]

This is defensive handling of already-duplicated stored data, not a claim that creating subscriptions with the same name produces duplicate IDs.

Scope and behavior

  • Normalize the shared reader so subscription rows and other index consumers receive unique GUIDs.
  • Preserve the first occurrence and existing order, and keep returning a mutable list for current callers.
  • Do not change subscription records, names, IDs, Compose keys, or the persisted format.
  • Do not write back during reads. Existing normal index writes can subsequently persist the deduplicated list.
  • Preserve distinct subscriptions with equal or blank names and the existing empty/malformed-index fallback.

No UI, localization, native-library, or dependency changes. Based directly on current upstream/master; independent of the accessibility changes in #6158.

Validation

  • Ran the six new regression tests against unchanged upstream: three failed, exposing repeated decoded IDs/rows and a surviving duplicate after removal.
  • With the fix, all six pass: :app:testPlaystoreDebugUnitTest --tests com.v2ray.ang.handler.SubscriptionIndexTest.
  • Full :app:testPlaystoreDebugUnitTest: 63 tests passed.
  • :app:compilePlaystoreDebugKotlin and :app:assemblePlaystoreDebug: passed.
  • API 37.1 Pixel 9 Pro emulator: injected repeated GUIDs into real MMKV storage; verified exactly three rows in first-occurrence order, correct edit target, and safe list reopening. A subsequent device-side assertion confirmed that the raw saved index still contained duplicates while both readers returned unique IDs. No app crash was observed.
  • Removed the temporary fixtures and restored the emulator's pre-test APK and original subscription index.

A saved SUB_IDS array can contain the same GUID more than once. Loading
it verbatim creates repeated subscription rows with identical Compose
and reorderable-list keys, which can crash the subscription screen.

Deduplicate IDs in the shared reader while preserving the first-seen
order and mutable-list contract. This protects every index consumer
without changing subscription records, generating replacement IDs, or
adding writes during reads. Different subscriptions with identical or
blank names remain separate.

Add six regression tests for duplicate rows, stable ordering, read-only
decoding, mutable round trips, repeated names, and existing empty/invalid
input behavior. Three assertions fail on unchanged upstream; all six
pass with the fix. The full 63-test suite and Play Store debug build pass.

On the API 37.1 Pixel 9 Pro emulator, a deliberately duplicated saved
index loaded each subscription once, retained first-occurrence order,
reopened safely, and edited the correct record. The raw index remained
duplicated throughout the check; no app crash was observed.
@2dust

2dust commented Sep 5, 2026

Copy link
Copy Markdown
Owner

会存在重复的ID 吗?
先合并

@2dust
2dust merged commit f4f992e into 2dust:master Sep 5, 2026
1 check passed
@eliotcougar
eliotcougar deleted the codex/fix-subscription-index-duplicates branch September 5, 2026 11:00
Maolaohei pushed a commit to Maolaohei/v2rayNG that referenced this pull request Sep 7, 2026
A saved SUB_IDS array can contain the same GUID more than once. Loading
it verbatim creates repeated subscription rows with identical Compose
and reorderable-list keys, which can crash the subscription screen.

Deduplicate IDs in the shared reader while preserving the first-seen
order and mutable-list contract. This protects every index consumer
without changing subscription records, generating replacement IDs, or
adding writes during reads. Different subscriptions with identical or
blank names remain separate.

Add six regression tests for duplicate rows, stable ordering, read-only
decoding, mutable round trips, repeated names, and existing empty/invalid
input behavior. Three assertions fail on unchanged upstream; all six
pass with the fix. The full 63-test suite and Play Store debug build pass.

On the API 37.1 Pixel 9 Pro emulator, a deliberately duplicated saved
index loaded each subscription once, retained first-occurrence order,
reopened safely, and edited the correct record. The raw index remained
duplicated throughout the check; no app crash was observed.

(cherry picked from commit f4f992e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants