From 2e49f00e29d63a1c31e4115a9baafc75d2d35885 Mon Sep 17 00:00:00 2001 From: Kangping Dong Date: Mon, 9 Jun 2025 20:05:49 +0800 Subject: [PATCH] [Thread] fix SparseArray hash for ActiveOperationalDataset The existing ActiveOperationalDataset#hashCode has an issue that it's depending on the SparseArray#hasCode which doesn't override the Object#hashCode method. The consequence is that when the ActiveOperationalDataset is used as a key in a container, there will be a miss even if two ActiveOperationalDataset objects have the same content. This commit fixes this issue by using a dedicated byteArraySparseArrayHasCode() method which computes the content hashcode with Arrays.deepHashCode(). Also fixes the issue that mUnknownTlvs is not counted in hashCode(). Flag: EXEMPT trivial bugfix Change-Id: I085d3bf0bdf4bb73bb995fc28dd3857437f9743e --- .../net/thread/ActiveOperationalDataset.java | 14 ++++++++++++-- .../thread/cts/ActiveOperationalDatasetTest.java | 9 +++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/thread/framework/java/android/net/thread/ActiveOperationalDataset.java b/thread/framework/java/android/net/thread/ActiveOperationalDataset.java index 1b50ba7769..cd256c9991 100644 --- a/thread/framework/java/android/net/thread/ActiveOperationalDataset.java +++ b/thread/framework/java/android/net/thread/ActiveOperationalDataset.java @@ -363,6 +363,14 @@ public final class ActiveOperationalDataset implements Parcelable { outputStream.write(entries, 0, entries.length); } + private static int byteArraySparseArrayHashCode(@NonNull SparseArray array) { + int result = 0; + for (int i = 0; i < array.size(); i++) { + result = 31 * result + deepHashCode(array.keyAt(i), array.valueAt(i)); + } + return result; + } + private static boolean areByteSparseArraysEqual( @NonNull SparseArray first, @NonNull SparseArray second) { if (first == second) { @@ -591,14 +599,16 @@ public final class ActiveOperationalDataset implements Parcelable { @Override public int hashCode() { - return deepHashCode( + // SparseArray is not hashable + int result = byteArraySparseArrayHashCode(mChannelMask); + result = result * 31 + byteArraySparseArrayHashCode(mUnknownTlvs); + return result * 31 + deepHashCode( mActiveTimestamp, mNetworkName, mExtendedPanId, mPanId, mChannel, mChannelPage, - mChannelMask, mPskc, mNetworkKey, mMeshLocalPrefix, diff --git a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java index 996d22d2e6..7a1b6fd86c 100644 --- a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java +++ b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java @@ -696,6 +696,15 @@ public final class ActiveOperationalDatasetTest { assertThat(dataset.getSecurityPolicy().getFlags()).isEqualTo(new byte[] {(byte) 0xff}); } + @Test + public void hashCode_datasetsWithUnknownTlvs_hashCodeEquals() { + final byte[] datasetWithUnknownTlvs = addTlv(VALID_DATASET_TLVS, "AA01FFBB020102"); + var dataset1 = ActiveOperationalDataset.fromThreadTlvs(datasetWithUnknownTlvs); + var dataset2 = ActiveOperationalDataset.fromThreadTlvs(datasetWithUnknownTlvs); + + assertThat(dataset1.hashCode()).isEqualTo(dataset2.hashCode()); + } + @Test public void securityPolicy_invalidRotationTime_throwsIllegalArguments() { assertThrows(