summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author George Lu <georgelu@google.com> 2019-07-31 14:26:30 -0700
committer George Lu <georgelu@google.com> 2019-08-02 11:05:42 -0700
commitbc2238ebcd318cf453bfea669ce3a1130b7c2106 (patch)
tree444503f21636084c72e7dcd6ef9e058b3841b68e
parent7bdc19bacb177ba5fbfd6bb5db6fe4543fe2f220 (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.java26
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java60
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());
- // }
}