Loading core/api/test-current.txt +4 −0 Original line number Diff line number Diff line Loading @@ -2958,6 +2958,10 @@ package android.service.notification { method @Deprecated public boolean isBound(); } public class NotificationRankingUpdate implements android.os.Parcelable { method public final boolean isFdNotNullAndClosed(); } } package android.service.quickaccesswallet { Loading core/java/android/service/notification/NotificationRankingUpdate.java +128 −3 Original line number Diff line number Diff line Loading @@ -16,32 +16,117 @@ package android.service.notification; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SharedMemory; import android.system.ErrnoException; import android.system.OsConstants; import androidx.annotation.NonNull; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import java.nio.ByteBuffer; /** * Represents an update to notification rankings. * @hide */ @SuppressLint({"ParcelNotFinal", "ParcelCreator"}) @TestApi public class NotificationRankingUpdate implements Parcelable { private final NotificationListenerService.RankingMap mRankingMap; // The ranking map is stored in shared memory when parceled, for sending across the binder. // This is done because the ranking map can grow large if there are many notifications. private SharedMemory mRankingMapFd = null; private final String mSharedMemoryName = "NotificationRankingUpdatedSharedMemory"; /** * @hide */ public NotificationRankingUpdate(NotificationListenerService.Ranking[] rankings) { mRankingMap = new NotificationListenerService.RankingMap(rankings); } /** * @hide */ public NotificationRankingUpdate(Parcel in) { mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class); if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { // Recover the ranking map from the SharedMemory and store it in mapParcel. final Parcel mapParcel = Parcel.obtain(); ByteBuffer buffer = null; try { // The ranking map should be stored in shared memory when it is parceled, so we // unwrap the SharedMemory object. mRankingMapFd = in.readParcelable(getClass().getClassLoader(), SharedMemory.class); // In the case that the ranking map can't be read, readParcelable may return null. // In this case, we set mRankingMap to null; if (mRankingMapFd == null) { mRankingMap = null; return; } // We only need read-only access to the shared memory region. buffer = mRankingMapFd.mapReadOnly(); if (buffer == null) { mRankingMap = null; return; } byte[] payload = new byte[buffer.remaining()]; buffer.get(payload); mapParcel.unmarshall(payload, 0, payload.length); mapParcel.setDataPosition(0); mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class); } catch (ErrnoException e) { // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to // avoid crashes; change to Log.wtf. throw new RuntimeException(e); } finally { mapParcel.recycle(); if (buffer != null) { mRankingMapFd.unmap(buffer); } } } else { mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class); } } /** * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing. * @hide */ @TestApi public final boolean isFdNotNullAndClosed() { return mRankingMapFd != null && mRankingMapFd.getFd() == -1; } /** * @hide */ public NotificationListenerService.RankingMap getRankingMap() { return mRankingMap; } /** * @hide */ @Override public int describeContents() { return 0; } /** * @hide */ @Override public boolean equals(@Nullable Object o) { if (this == o) return true; Loading @@ -51,11 +136,51 @@ public class NotificationRankingUpdate implements Parcelable { return mRankingMap.equals(other.mRankingMap); } /** * @hide */ @Override public void writeToParcel(Parcel out, int flags) { public void writeToParcel(@NonNull Parcel out, int flags) { if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { final Parcel mapParcel = Parcel.obtain(); try { // Parcels the ranking map and measures its size. mapParcel.writeParcelable(mRankingMap, flags); int mapSize = mapParcel.dataSize(); // Creates a new SharedMemory object with enough space to hold the ranking map. SharedMemory mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize); if (mRankingMapFd == null) { return; } // Gets a read/write buffer mapping the entire shared memory region. final ByteBuffer buffer = mRankingMapFd.mapReadWrite(); // Puts the ranking map into the shared memory region buffer. buffer.put(mapParcel.marshall(), 0, mapSize); // Protects the region from being written to, by setting it to be read-only. mRankingMapFd.setProtect(OsConstants.PROT_READ); // Puts the SharedMemory object in the parcel. out.writeParcelable(mRankingMapFd, flags); } catch (ErrnoException e) { // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to // avoid crashes; change to Log.wtf. throw new RuntimeException(e); } finally { mapParcel.recycle(); } } else { out.writeParcelable(mRankingMap, flags); } } /** * @hide */ public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR = new Parcelable.Creator<NotificationRankingUpdate>() { public NotificationRankingUpdate createFromParcel(Parcel parcel) { Loading core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +4 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,10 @@ public class SystemUiSystemPropertiesFlags { /** Gating the holding of WakeLocks until NLSes are told about a new notification. */ public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION = devFlag("persist.sysui.notification.wake_lock_for_posting_notification"); /** Gating storing NotificationRankingUpdate ranking map in shared memory. */ public static final Flag RANKING_UPDATE_ASHMEM = devFlag( "persist.sysui.notification.ranking_update_ashmem"); } //// == End of flags. Everything below this line is the implementation. == //// Loading core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java 0 → 100644 +195 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.notification; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import android.app.NotificationChannel; import android.app.NotificationManager; import android.os.Parcel; import androidx.test.filters.SmallTest; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @SmallTest @RunWith(Parameterized.class) public class NotificationRankingUpdateTest { private static final String NOTIFICATION_CHANNEL_ID = "test_channel_id"; private static final String TEST_KEY = "key"; private NotificationChannel mNotificationChannel; // TODO(b/284297289): remove this flag set once resolved. @Parameterized.Parameters(name = "rankingUpdateAshmem={0}") public static Boolean[] getRankingUpdateAshmem() { return new Boolean[] { true, false }; } @Parameterized.Parameter public boolean mRankingUpdateAshmem; @Before public void setUp() { mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT); SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> { if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) { return mRankingUpdateAshmem; } return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag); }; } @After public void tearDown() { SystemUiSystemPropertiesFlags.TEST_RESOLVER = null; } public NotificationListenerService.Ranking createTestRanking(String key, int rank) { NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); ranking.populate( /* key= */ key, /* rank= */ rank, /* matchesInterruptionFilter= */ false, /* visibilityOverride= */ 0, /* suppressedVisualEffects= */ 0, mNotificationChannel.getImportance(), /* explanation= */ null, /* overrideGroupKey= */ null, mNotificationChannel, /* overridePeople= */ null, /* snoozeCriteria= */ null, /* showBadge= */ true, /* userSentiment= */ 0, /* hidden= */ false, /* lastAudiblyAlertedMs= */ -1, /* noisy= */ false, /* smartActions= */ null, /* smartReplies= */ null, /* canBubble= */ false, /* isTextChanged= */ false, /* isConversation= */ false, /* shortcutInfo= */ null, /* rankingAdjustment= */ 0, /* isBubble= */ false, /* proposedImportance= */ 0, /* sensitiveContent= */ false ); return ranking; } @Test public void testRankingUpdate_rankingConstructor() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); NotificationListenerService.RankingMap retrievedRankings = rankingUpdate.getRankingMap(); NotificationListenerService.Ranking retrievedRanking = new NotificationListenerService.Ranking(); assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking)); assertEquals(123, retrievedRanking.getRank()); } @Test public void testRankingUpdate_parcelConstructor() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); Parcel parceledRankingUpdate = Parcel.obtain(); rankingUpdate.writeToParcel(parceledRankingUpdate, 0); parceledRankingUpdate.setDataPosition(0); NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate( parceledRankingUpdate); NotificationListenerService.RankingMap retrievedRankings = retrievedRankingUpdate.getRankingMap(); assertNotNull(retrievedRankings); assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed()); NotificationListenerService.Ranking retrievedRanking = new NotificationListenerService.Ranking(); assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking)); assertEquals(123, retrievedRanking.getRank()); assertTrue(retrievedRankingUpdate.equals(rankingUpdate)); parceledRankingUpdate.recycle(); } @Test public void testRankingUpdate_emptyParcelInCheck() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); Parcel parceledRankingUpdate = Parcel.obtain(); rankingUpdate.writeToParcel(parceledRankingUpdate, 0); // This will fail to read the parceledRankingUpdate, because the data position hasn't // been reset, so it'll find no data to read. NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate( parceledRankingUpdate); assertNull(retrievedRankingUpdate.getRankingMap()); } @Test public void testRankingUpdate_describeContents() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); assertEquals(0, rankingUpdate.describeContents()); } @Test public void testRankingUpdate_equals() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); // Reflexive equality. assertTrue(rankingUpdate.equals(rankingUpdate)); // Null or wrong class inequality. assertFalse(rankingUpdate.equals(null)); assertFalse(rankingUpdate.equals(ranking)); // Different ranking contents inequality. NotificationListenerService.Ranking ranking2 = createTestRanking(TEST_KEY, 456); NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking2}); assertFalse(rankingUpdate.equals(rankingUpdate2)); // Same ranking contents equality. ranking2 = createTestRanking(TEST_KEY, 123); rankingUpdate2 = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking2}); assertTrue(rankingUpdate.equals(rankingUpdate2)); } } Loading
core/api/test-current.txt +4 −0 Original line number Diff line number Diff line Loading @@ -2958,6 +2958,10 @@ package android.service.notification { method @Deprecated public boolean isBound(); } public class NotificationRankingUpdate implements android.os.Parcelable { method public final boolean isFdNotNullAndClosed(); } } package android.service.quickaccesswallet { Loading
core/java/android/service/notification/NotificationRankingUpdate.java +128 −3 Original line number Diff line number Diff line Loading @@ -16,32 +16,117 @@ package android.service.notification; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SharedMemory; import android.system.ErrnoException; import android.system.OsConstants; import androidx.annotation.NonNull; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import java.nio.ByteBuffer; /** * Represents an update to notification rankings. * @hide */ @SuppressLint({"ParcelNotFinal", "ParcelCreator"}) @TestApi public class NotificationRankingUpdate implements Parcelable { private final NotificationListenerService.RankingMap mRankingMap; // The ranking map is stored in shared memory when parceled, for sending across the binder. // This is done because the ranking map can grow large if there are many notifications. private SharedMemory mRankingMapFd = null; private final String mSharedMemoryName = "NotificationRankingUpdatedSharedMemory"; /** * @hide */ public NotificationRankingUpdate(NotificationListenerService.Ranking[] rankings) { mRankingMap = new NotificationListenerService.RankingMap(rankings); } /** * @hide */ public NotificationRankingUpdate(Parcel in) { mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class); if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { // Recover the ranking map from the SharedMemory and store it in mapParcel. final Parcel mapParcel = Parcel.obtain(); ByteBuffer buffer = null; try { // The ranking map should be stored in shared memory when it is parceled, so we // unwrap the SharedMemory object. mRankingMapFd = in.readParcelable(getClass().getClassLoader(), SharedMemory.class); // In the case that the ranking map can't be read, readParcelable may return null. // In this case, we set mRankingMap to null; if (mRankingMapFd == null) { mRankingMap = null; return; } // We only need read-only access to the shared memory region. buffer = mRankingMapFd.mapReadOnly(); if (buffer == null) { mRankingMap = null; return; } byte[] payload = new byte[buffer.remaining()]; buffer.get(payload); mapParcel.unmarshall(payload, 0, payload.length); mapParcel.setDataPosition(0); mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class); } catch (ErrnoException e) { // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to // avoid crashes; change to Log.wtf. throw new RuntimeException(e); } finally { mapParcel.recycle(); if (buffer != null) { mRankingMapFd.unmap(buffer); } } } else { mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class); } } /** * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing. * @hide */ @TestApi public final boolean isFdNotNullAndClosed() { return mRankingMapFd != null && mRankingMapFd.getFd() == -1; } /** * @hide */ public NotificationListenerService.RankingMap getRankingMap() { return mRankingMap; } /** * @hide */ @Override public int describeContents() { return 0; } /** * @hide */ @Override public boolean equals(@Nullable Object o) { if (this == o) return true; Loading @@ -51,11 +136,51 @@ public class NotificationRankingUpdate implements Parcelable { return mRankingMap.equals(other.mRankingMap); } /** * @hide */ @Override public void writeToParcel(Parcel out, int flags) { public void writeToParcel(@NonNull Parcel out, int flags) { if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { final Parcel mapParcel = Parcel.obtain(); try { // Parcels the ranking map and measures its size. mapParcel.writeParcelable(mRankingMap, flags); int mapSize = mapParcel.dataSize(); // Creates a new SharedMemory object with enough space to hold the ranking map. SharedMemory mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize); if (mRankingMapFd == null) { return; } // Gets a read/write buffer mapping the entire shared memory region. final ByteBuffer buffer = mRankingMapFd.mapReadWrite(); // Puts the ranking map into the shared memory region buffer. buffer.put(mapParcel.marshall(), 0, mapSize); // Protects the region from being written to, by setting it to be read-only. mRankingMapFd.setProtect(OsConstants.PROT_READ); // Puts the SharedMemory object in the parcel. out.writeParcelable(mRankingMapFd, flags); } catch (ErrnoException e) { // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to // avoid crashes; change to Log.wtf. throw new RuntimeException(e); } finally { mapParcel.recycle(); } } else { out.writeParcelable(mRankingMap, flags); } } /** * @hide */ public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR = new Parcelable.Creator<NotificationRankingUpdate>() { public NotificationRankingUpdate createFromParcel(Parcel parcel) { Loading
core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +4 −0 Original line number Diff line number Diff line Loading @@ -85,6 +85,10 @@ public class SystemUiSystemPropertiesFlags { /** Gating the holding of WakeLocks until NLSes are told about a new notification. */ public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION = devFlag("persist.sysui.notification.wake_lock_for_posting_notification"); /** Gating storing NotificationRankingUpdate ranking map in shared memory. */ public static final Flag RANKING_UPDATE_ASHMEM = devFlag( "persist.sysui.notification.ranking_update_ashmem"); } //// == End of flags. Everything below this line is the implementation. == //// Loading
core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java 0 → 100644 +195 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.notification; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import android.app.NotificationChannel; import android.app.NotificationManager; import android.os.Parcel; import androidx.test.filters.SmallTest; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @SmallTest @RunWith(Parameterized.class) public class NotificationRankingUpdateTest { private static final String NOTIFICATION_CHANNEL_ID = "test_channel_id"; private static final String TEST_KEY = "key"; private NotificationChannel mNotificationChannel; // TODO(b/284297289): remove this flag set once resolved. @Parameterized.Parameters(name = "rankingUpdateAshmem={0}") public static Boolean[] getRankingUpdateAshmem() { return new Boolean[] { true, false }; } @Parameterized.Parameter public boolean mRankingUpdateAshmem; @Before public void setUp() { mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT); SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> { if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) { return mRankingUpdateAshmem; } return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag); }; } @After public void tearDown() { SystemUiSystemPropertiesFlags.TEST_RESOLVER = null; } public NotificationListenerService.Ranking createTestRanking(String key, int rank) { NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); ranking.populate( /* key= */ key, /* rank= */ rank, /* matchesInterruptionFilter= */ false, /* visibilityOverride= */ 0, /* suppressedVisualEffects= */ 0, mNotificationChannel.getImportance(), /* explanation= */ null, /* overrideGroupKey= */ null, mNotificationChannel, /* overridePeople= */ null, /* snoozeCriteria= */ null, /* showBadge= */ true, /* userSentiment= */ 0, /* hidden= */ false, /* lastAudiblyAlertedMs= */ -1, /* noisy= */ false, /* smartActions= */ null, /* smartReplies= */ null, /* canBubble= */ false, /* isTextChanged= */ false, /* isConversation= */ false, /* shortcutInfo= */ null, /* rankingAdjustment= */ 0, /* isBubble= */ false, /* proposedImportance= */ 0, /* sensitiveContent= */ false ); return ranking; } @Test public void testRankingUpdate_rankingConstructor() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); NotificationListenerService.RankingMap retrievedRankings = rankingUpdate.getRankingMap(); NotificationListenerService.Ranking retrievedRanking = new NotificationListenerService.Ranking(); assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking)); assertEquals(123, retrievedRanking.getRank()); } @Test public void testRankingUpdate_parcelConstructor() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); Parcel parceledRankingUpdate = Parcel.obtain(); rankingUpdate.writeToParcel(parceledRankingUpdate, 0); parceledRankingUpdate.setDataPosition(0); NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate( parceledRankingUpdate); NotificationListenerService.RankingMap retrievedRankings = retrievedRankingUpdate.getRankingMap(); assertNotNull(retrievedRankings); assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed()); NotificationListenerService.Ranking retrievedRanking = new NotificationListenerService.Ranking(); assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking)); assertEquals(123, retrievedRanking.getRank()); assertTrue(retrievedRankingUpdate.equals(rankingUpdate)); parceledRankingUpdate.recycle(); } @Test public void testRankingUpdate_emptyParcelInCheck() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); Parcel parceledRankingUpdate = Parcel.obtain(); rankingUpdate.writeToParcel(parceledRankingUpdate, 0); // This will fail to read the parceledRankingUpdate, because the data position hasn't // been reset, so it'll find no data to read. NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate( parceledRankingUpdate); assertNull(retrievedRankingUpdate.getRankingMap()); } @Test public void testRankingUpdate_describeContents() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); assertEquals(0, rankingUpdate.describeContents()); } @Test public void testRankingUpdate_equals() { NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking}); // Reflexive equality. assertTrue(rankingUpdate.equals(rankingUpdate)); // Null or wrong class inequality. assertFalse(rankingUpdate.equals(null)); assertFalse(rankingUpdate.equals(ranking)); // Different ranking contents inequality. NotificationListenerService.Ranking ranking2 = createTestRanking(TEST_KEY, 456); NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking2}); assertFalse(rankingUpdate.equals(rankingUpdate2)); // Same ranking contents equality. ranking2 = createTestRanking(TEST_KEY, 123); rankingUpdate2 = new NotificationRankingUpdate( new NotificationListenerService.Ranking[]{ranking2}); assertTrue(rankingUpdate.equals(rankingUpdate2)); } }