diff options
| author | 2019-07-31 14:26:30 -0700 | |
|---|---|---|
| committer | 2019-08-02 11:05:42 -0700 | |
| commit | bc2238ebcd318cf453bfea669ce3a1130b7c2106 (patch) | |
| tree | 444503f21636084c72e7dcd6ef9e058b3841b68e | |
| parent | 7bdc19bacb177ba5fbfd6bb5db6fe4543fe2f220 (diff) | |
Fix RadioMetadata.hashCode().
ag/7063548 implemented RadioMetadata.hashCode() by calling Bundle.hashCode(),
but that function uses the default implementation that uses the address
of the Bundle. As a result, two RadioMetadata objects that were equal
probably had different hash codes.
RadioMetadata now instead lazily computes its hash code based upon the keys and
values in mBundle.
Fixes: 130750904
Test: atest com.android.server.broadcastradio.hal2.StartProgramListUpdatesFanoutTest
Change-Id: Ia49716594744871831bc3734aea37e0f505897bf
| -rw-r--r-- | core/java/android/hardware/radio/RadioMetadata.java | 26 | ||||
| -rw-r--r-- | core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java | 60 |
2 files changed, 26 insertions, 60 deletions
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java index c135c8a73abc..76304bda05a1 100644 --- a/core/java/android/hardware/radio/RadioMetadata.java +++ b/core/java/android/hardware/radio/RadioMetadata.java @@ -26,6 +26,9 @@ import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Set; /** @@ -257,9 +260,22 @@ public final class RadioMetadata implements Parcelable { private final Bundle mBundle; + // Lazily computed hash code based upon the contents of mBundle. + private Integer mHashCode; + @Override public int hashCode() { - return mBundle.hashCode(); + if (mHashCode == null) { + List<String> keys = new ArrayList<String>(mBundle.keySet()); + keys.sort(null); + Object[] objs = new Object[2 * keys.size()]; + for (int i = 0; i < keys.size(); i++) { + objs[2 * i] = keys.get(i); + objs[2 * i + 1] = mBundle.get(keys.get(i)); + } + mHashCode = Arrays.hashCode(objs); + } + return mHashCode; } @Override @@ -626,6 +642,8 @@ public final class RadioMetadata implements Parcelable { String key = getKeyFromNativeKey(nativeKey); try { putInt(mBundle, key, value); + // Invalidate mHashCode to force it to be recomputed. + mHashCode = null; return 0; } catch (IllegalArgumentException ex) { return -1; @@ -639,6 +657,8 @@ public final class RadioMetadata implements Parcelable { return -1; } mBundle.putString(key, value); + // Invalidate mHashCode to force it to be recomputed. + mHashCode = null; return 0; } @@ -653,6 +673,8 @@ public final class RadioMetadata implements Parcelable { bmp = BitmapFactory.decodeByteArray(value, 0, value.length); if (bmp != null) { mBundle.putParcelable(key, bmp); + // Invalidate mHashCode to force it to be recomputed. + mHashCode = null; return 0; } } catch (Exception e) { @@ -668,6 +690,8 @@ public final class RadioMetadata implements Parcelable { } mBundle.putParcelable(key, new RadioMetadata.Clock( utcEpochSeconds, timezoneOffsetInMinutes)); + // Invalidate mHashCode to force it to be recomputed. + mHashCode = null; return 0; } } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java index f9e37981fa6f..6e65df1bafb6 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java @@ -17,7 +17,6 @@ package com.android.server.broadcastradio.hal2; import static org.junit.Assert.*; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -41,7 +40,6 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; @@ -49,7 +47,6 @@ import org.mockito.stubbing.Answer; import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.Set; /** * Tests for v2 HAL RadioModule. @@ -293,61 +290,6 @@ public class StartProgramListUpdatesFanoutTest { } ProgramList.Chunk expectedChunk = new ProgramList.Chunk(purge, true, modifiedSet, removedSet); - verify(clientMock).onProgramListUpdated(argThat(new ChunkMatcher(expectedChunk))); + verify(clientMock).onProgramListUpdated(expectedChunk); } - - // TODO(b/130750904): Remove this class and replace "argThat(new ChunkMatcher(chunk))" with - // "eq(chunk)". - // - // Ideally, this class wouldn't exist, but currently RadioManager.ProgramInfo#hashCode() can - // return different values for objects that satisfy ProgramInfo#equals(). As a short term - // workaround, this class performs the O(N^2) comparison between the Chunks' mModified sets. - // - // To test if ProgramInfo#hashCode() has been fixed, remove commenting from - // testProgramInfoHashCode() below. - private class ChunkMatcher implements ArgumentMatcher<ProgramList.Chunk> { - private final ProgramList.Chunk mExpected; - - ChunkMatcher(ProgramList.Chunk expected) { - mExpected = expected; - } - - @Override - public boolean matches(ProgramList.Chunk actual) { - if ((mExpected.isPurge() != actual.isPurge()) - || (mExpected.isComplete() != actual.isComplete()) - || (!mExpected.getRemoved().equals(actual.getRemoved()))) { - return false; - } - Set<RadioManager.ProgramInfo> expectedModified = mExpected.getModified(); - Set<RadioManager.ProgramInfo> actualModified = new HashSet<>(actual.getModified()); - if (expectedModified.size() != actualModified.size()) { - return false; - } - for (RadioManager.ProgramInfo expectedInfo : expectedModified) { - boolean found = false; - for (RadioManager.ProgramInfo actualInfo : actualModified) { - if (expectedInfo.equals(actualInfo)) { - found = true; - actualModified.remove(actualInfo); - break; - } - } - if (!found) { - return false; - } - } - return true; - } - } - - // @Test - // public void testProgramInfoHashCode() { - // RadioManager.ProgramInfo info1 = TestUtils.makeProgramInfo( - // ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0); - // RadioManager.ProgramInfo info2 = TestUtils.makeProgramInfo( - // ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0); - // assertEquals(info1, info2); - // assertEquals(info1.hashCode(), info2.hashCode()); - // } } |