Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e27d039 authored by Weilin Xu's avatar Weilin Xu Committed by Android (Google) Code Review
Browse files

Merge changes from topic "use-radio-unique-program-id" into main

* changes:
  Update unit tests for program cache with unique id
  Use unique identifier in radio manager and service
parents d7aaede8 fa41f438
Loading
Loading
Loading
Loading
+67 −29
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.ArraySet;

import com.android.internal.annotations.GuardedBy;

@@ -34,7 +35,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * @hide
@@ -45,8 +45,8 @@ public final class ProgramList implements AutoCloseable {
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private final Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> mPrograms =
            new ArrayMap<>();
    private final Map<ProgramSelector.Identifier, Map<UniqueProgramIdentifier,
            RadioManager.ProgramInfo>> mPrograms = new ArrayMap<>();

    @GuardedBy("mLock")
    private final List<ListCallback> mListCallbacks = new ArrayList<>();
@@ -193,7 +193,7 @@ public final class ProgramList implements AutoCloseable {

    void apply(Chunk chunk) {
        List<ProgramSelector.Identifier> removedList = new ArrayList<>();
        List<ProgramSelector.Identifier> changedList = new ArrayList<>();
        Set<ProgramSelector.Identifier> changedSet = new ArraySet<>();
        List<ProgramList.ListCallback> listCallbacksCopied;
        List<OnCompleteListener> onCompleteListenersCopied = new ArrayList<>();
        synchronized (mLock) {
@@ -203,19 +203,27 @@ public final class ProgramList implements AutoCloseable {
            listCallbacksCopied = new ArrayList<>(mListCallbacks);

            if (chunk.isPurge()) {
                Iterator<Map.Entry<ProgramSelector.Identifier, RadioManager.ProgramInfo>>
                        programsIterator = mPrograms.entrySet().iterator();
                Iterator<Map.Entry<ProgramSelector.Identifier, Map<UniqueProgramIdentifier,
                        RadioManager.ProgramInfo>>> programsIterator =
                        mPrograms.entrySet().iterator();
                while (programsIterator.hasNext()) {
                    RadioManager.ProgramInfo removed = programsIterator.next().getValue();
                    if (removed != null) {
                        removedList.add(removed.getSelector().getPrimaryId());
                    Map.Entry<ProgramSelector.Identifier, Map<UniqueProgramIdentifier,
                            RadioManager.ProgramInfo>> removed = programsIterator.next();
                    if (removed.getValue() != null) {
                        removedList.add(removed.getKey());
                    }
                    programsIterator.remove();
                }
            }

            chunk.getRemoved().stream().forEach(id -> removeLocked(id, removedList));
            chunk.getModified().stream().forEach(info -> putLocked(info, changedList));
            Iterator<UniqueProgramIdentifier> removedIterator = chunk.getRemoved().iterator();
            while (removedIterator.hasNext()) {
                removeLocked(removedIterator.next(), removedList);
            }
            Iterator<RadioManager.ProgramInfo> modifiedIterator = chunk.getModified().iterator();
            while (modifiedIterator.hasNext()) {
                putLocked(modifiedIterator.next(), changedSet);
            }

            if (chunk.isComplete()) {
                mIsComplete = true;
@@ -228,9 +236,11 @@ public final class ProgramList implements AutoCloseable {
                listCallbacksCopied.get(cbIndex).onItemRemoved(removedList.get(i));
            }
        }
        for (int i = 0; i < changedList.size(); i++) {
        Iterator<ProgramSelector.Identifier> changedIterator = changedSet.iterator();
        while (changedIterator.hasNext()) {
            ProgramSelector.Identifier changedId = changedIterator.next();
            for (int cbIndex = 0; cbIndex < listCallbacksCopied.size(); cbIndex++) {
                listCallbacksCopied.get(cbIndex).onItemChanged(changedList.get(i));
                listCallbacksCopied.get(cbIndex).onItemChanged(changedId);
            }
        }
        if (chunk.isComplete()) {
@@ -242,20 +252,31 @@ public final class ProgramList implements AutoCloseable {

    @GuardedBy("mLock")
    private void putLocked(RadioManager.ProgramInfo value,
            List<ProgramSelector.Identifier> changedIdentifierList) {
        ProgramSelector.Identifier key = value.getSelector().getPrimaryId();
        mPrograms.put(Objects.requireNonNull(key), value);
        ProgramSelector.Identifier sel = value.getSelector().getPrimaryId();
        changedIdentifierList.add(sel);
            Set<ProgramSelector.Identifier> changedIdentifierSet) {
        UniqueProgramIdentifier key = new UniqueProgramIdentifier(
                value.getSelector());
        ProgramSelector.Identifier primaryKey = Objects.requireNonNull(key.getPrimaryId());
        if (!mPrograms.containsKey(primaryKey)) {
            mPrograms.put(primaryKey, new ArrayMap<>());
        }
        mPrograms.get(primaryKey).put(key, value);
        changedIdentifierSet.add(primaryKey);
    }

    @GuardedBy("mLock")
    private void removeLocked(ProgramSelector.Identifier key,
    private void removeLocked(UniqueProgramIdentifier key,
            List<ProgramSelector.Identifier> removedIdentifierList) {
        RadioManager.ProgramInfo removed = mPrograms.remove(Objects.requireNonNull(key));
        ProgramSelector.Identifier primaryKey = Objects.requireNonNull(key.getPrimaryId());
        if (!mPrograms.containsKey(primaryKey)) {
            return;
        }
        Map<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries = mPrograms
                .get(primaryKey);
        RadioManager.ProgramInfo removed = entries.remove(Objects.requireNonNull(key));
        if (removed == null) return;
        ProgramSelector.Identifier sel = removed.getSelector().getPrimaryId();
        removedIdentifierList.add(sel);
        if (entries.size() == 0) {
            removedIdentifierList.add(primaryKey);
        }
    }

    /**
@@ -264,10 +285,21 @@ public final class ProgramList implements AutoCloseable {
     * @return the new List<> object; it won't receive any further updates
     */
    public @NonNull List<RadioManager.ProgramInfo> toList() {
        List<RadioManager.ProgramInfo> list = new ArrayList<>();
        synchronized (mLock) {
            return mPrograms.values().stream().collect(Collectors.toList());
            Iterator<Map.Entry<ProgramSelector.Identifier, Map<UniqueProgramIdentifier,
                    RadioManager.ProgramInfo>>> listIterator = mPrograms.entrySet().iterator();
            while (listIterator.hasNext()) {
                Iterator<Map.Entry<UniqueProgramIdentifier,
                        RadioManager.ProgramInfo>> prorgramsIterator = listIterator.next()
                        .getValue().entrySet().iterator();
                while (prorgramsIterator.hasNext()) {
                    list.add(prorgramsIterator.next().getValue());
                }
            }
        }
        return list;
    }

    /**
     * Returns the program with a specified primary identifier.
@@ -276,9 +308,15 @@ public final class ProgramList implements AutoCloseable {
     * @return the program info, or null if there is no such program on the list
     */
    public @Nullable RadioManager.ProgramInfo get(@NonNull ProgramSelector.Identifier id) {
        Map<UniqueProgramIdentifier, RadioManager.ProgramInfo> entries;
        synchronized (mLock) {
            return mPrograms.get(Objects.requireNonNull(id));
            entries = mPrograms.get(Objects.requireNonNull(id,
                    "Primary identifier can not be null"));
        }
        if (entries == null) {
            return null;
        }
        return entries.entrySet().iterator().next().getValue();
    }

    /**
@@ -404,7 +442,7 @@ public final class ProgramList implements AutoCloseable {
         * Checks, if non-tunable entries that define tree structure on the
         * program list (i.e. DAB ensembles) should be included.
         *
         * @see {@link ProgramSelector.Identifier#isCategory()}
         * @see ProgramSelector.Identifier#isCategoryType()
         */
        public boolean areCategoriesIncluded() {
            return mIncludeCategories;
@@ -459,11 +497,11 @@ public final class ProgramList implements AutoCloseable {
        private final boolean mPurge;
        private final boolean mComplete;
        private final @NonNull Set<RadioManager.ProgramInfo> mModified;
        private final @NonNull Set<ProgramSelector.Identifier> mRemoved;
        private final @NonNull Set<UniqueProgramIdentifier> mRemoved;

        public Chunk(boolean purge, boolean complete,
                @Nullable Set<RadioManager.ProgramInfo> modified,
                @Nullable Set<ProgramSelector.Identifier> removed) {
                @Nullable Set<UniqueProgramIdentifier> removed) {
            mPurge = purge;
            mComplete = complete;
            mModified = (modified != null) ? modified : Collections.emptySet();
@@ -474,7 +512,7 @@ public final class ProgramList implements AutoCloseable {
            mPurge = in.readByte() != 0;
            mComplete = in.readByte() != 0;
            mModified = Utils.createSet(in, RadioManager.ProgramInfo.CREATOR);
            mRemoved = Utils.createSet(in, ProgramSelector.Identifier.CREATOR);
            mRemoved = Utils.createSet(in, UniqueProgramIdentifier.CREATOR);
        }

        @Override
@@ -512,7 +550,7 @@ public final class ProgramList implements AutoCloseable {
            return mModified;
        }

        public @NonNull Set<ProgramSelector.Identifier> getRemoved() {
        public @NonNull Set<UniqueProgramIdentifier> getRemoved() {
            return mRemoved;
        }

+113 −52
Original line number Diff line number Diff line
@@ -74,18 +74,45 @@ public final class ProgramListTest {
    private static final ProgramSelector.Identifier DAB_ENSEMBLE_IDENTIFIER =
            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
                    /* value= */ 0x1013);
    private static final ProgramSelector.Identifier DAB_FREQUENCY_IDENTIFIER_1 =
            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
                    /* value= */ 222_064);
    private static final ProgramSelector.Identifier DAB_FREQUENCY_IDENTIFIER_2 =
            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
                    /* value= */ 220_352);

    private static final ProgramSelector DAB_SELECTOR_1 = new ProgramSelector(
            ProgramSelector.PROGRAM_TYPE_DAB, DAB_DMB_SID_EXT_IDENTIFIER,
            new ProgramSelector.Identifier[]{DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER_1},
            /* vendorIds= */ null);
    private static final ProgramSelector DAB_SELECTOR_2 = new ProgramSelector(
            ProgramSelector.PROGRAM_TYPE_DAB, DAB_DMB_SID_EXT_IDENTIFIER,
            new ProgramSelector.Identifier[]{DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER_2},
            /* vendorIds= */ null);

    private static final UniqueProgramIdentifier RDS_UNIQUE_IDENTIFIER =
            new UniqueProgramIdentifier(RDS_IDENTIFIER);
    private static final UniqueProgramIdentifier DAB_UNIQUE_IDENTIFIER_1 =
            new UniqueProgramIdentifier(DAB_SELECTOR_1);
    private static final UniqueProgramIdentifier DAB_UNIQUE_IDENTIFIER_2 =
            new UniqueProgramIdentifier(DAB_SELECTOR_2);

    private static final RadioManager.ProgramInfo FM_PROGRAM_INFO = createFmProgramInfo(
            createProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, FM_IDENTIFIER));
    private static final RadioManager.ProgramInfo RDS_PROGRAM_INFO = createFmProgramInfo(
            createProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, RDS_IDENTIFIER));
    private static final RadioManager.ProgramInfo DAB_PROGRAM_INFO_1 = createDabProgramInfo(
            DAB_SELECTOR_1);
    private static final RadioManager.ProgramInfo DAB_PROGRAM_INFO_2 = createDabProgramInfo(
            DAB_SELECTOR_2);

    private static final Set<Integer> FILTER_IDENTIFIER_TYPES = Set.of(
            ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, ProgramSelector.IDENTIFIER_TYPE_RDS_PI);
    private static final Set<ProgramSelector.Identifier> FILTER_IDENTIFIERS = Set.of(FM_IDENTIFIER);

    private static final ProgramList.Chunk FM_RDS_ADD_CHUNK = new ProgramList.Chunk(IS_PURGE,
            IS_COMPLETE, Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
            Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
            ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
            ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT);
    private static final Set<ProgramSelector.Identifier> FILTER_IDENTIFIERS = Set.of(
            FM_IDENTIFIER, DAB_DMB_SID_EXT_IDENTIFIER);

    private static final ProgramList.Chunk FM_DAB_ADD_CHUNK = new ProgramList.Chunk(IS_PURGE,
            IS_COMPLETE, Set.of(FM_PROGRAM_INFO, DAB_PROGRAM_INFO_1, DAB_PROGRAM_INFO_2),
            Set.of(RDS_UNIQUE_IDENTIFIER));
    private static final ProgramList.Chunk FM_ADD_INCOMPLETE_CHUNK = new ProgramList.Chunk(IS_PURGE,
            /* complete= */ false, Set.of(FM_PROGRAM_INFO), new ArraySet<>());
    private static final ProgramList.Filter TEST_FILTER = new ProgramList.Filter(
@@ -213,58 +240,44 @@ public final class ProgramListTest {

    @Test
    public void isPurge_forChunk() {
        ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
                Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
                Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));

        assertWithMessage("Puring chunk").that(chunk.isPurge()).isEqualTo(IS_PURGE);
        assertWithMessage("Puring chunk").that(FM_DAB_ADD_CHUNK.isPurge()).isEqualTo(IS_PURGE);
    }

    @Test
    public void isComplete_forChunk() {
        ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
                Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
                Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));

        assertWithMessage("Complete chunk").that(chunk.isComplete()).isEqualTo(IS_COMPLETE);
        assertWithMessage("Complete chunk").that(FM_DAB_ADD_CHUNK.isComplete())
                .isEqualTo(IS_COMPLETE);
    }

    @Test
    public void getModified_forChunk() {
        ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
                Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
                Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));

        assertWithMessage("Modified program info in chunk")
                .that(chunk.getModified()).containsExactly(FM_PROGRAM_INFO, RDS_PROGRAM_INFO);
                .that(FM_DAB_ADD_CHUNK.getModified())
                .containsExactly(FM_PROGRAM_INFO, DAB_PROGRAM_INFO_1, DAB_PROGRAM_INFO_2);
    }

    @Test
    public void getRemoved_forChunk() {
        ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
                Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
                Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));

        assertWithMessage("Removed program identifiers in chunk").that(chunk.getRemoved())
                .containsExactly(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER);
        assertWithMessage("Removed program identifiers in chunk")
                .that(FM_DAB_ADD_CHUNK.getRemoved()).containsExactly(RDS_UNIQUE_IDENTIFIER);
    }

    @Test
    public void describeContents_forChunk() {
        assertWithMessage("Chunk contents").that(FM_RDS_ADD_CHUNK.describeContents()).isEqualTo(0);
        assertWithMessage("Chunk contents").that(FM_DAB_ADD_CHUNK.describeContents()).isEqualTo(0);
    }

    @Test
    public void writeToParcel_forChunk() {
        Parcel parcel = Parcel.obtain();

        FM_RDS_ADD_CHUNK.writeToParcel(parcel, /* flags= */ 0);
        FM_DAB_ADD_CHUNK.writeToParcel(parcel, /* flags= */ 0);
        parcel.setDataPosition(0);

        ProgramList.Chunk chunkFromParcel =
                ProgramList.Chunk.CREATOR.createFromParcel(parcel);
        assertWithMessage("Chunk created from parcel")
                .that(chunkFromParcel).isEqualTo(FM_RDS_ADD_CHUNK);
                .that(chunkFromParcel).isEqualTo(FM_DAB_ADD_CHUNK);
    }

    @Test
@@ -336,37 +349,78 @@ public final class ProgramListTest {
    }

    @Test
    public void onProgramListUpdated_withNewIdsAdded_invokesMockedCallbacks() throws Exception {
    public void onProgramListUpdated_withNewIdsAdded_invokesCallbacks() throws Exception {
        createRadioTuner();
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
        registerListCallbacks(/* numCallbacks= */ 1);
        addOnCompleteListeners(/* numListeners= */ 1);

        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(FM_IDENTIFIER);
        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(RDS_IDENTIFIER);
        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemChanged(DAB_DMB_SID_EXT_IDENTIFIER);
        verify(mOnCompleteListenerMocks[0], CALLBACK_TIMEOUT).onComplete();
        assertWithMessage("Program info in program list after adding FM and RDS info")
                .that(mProgramList.toList()).containsExactly(FM_PROGRAM_INFO, RDS_PROGRAM_INFO);
        assertWithMessage("Program info in program list after adding FM and DAB info")
                .that(mProgramList.toList()).containsExactly(FM_PROGRAM_INFO, DAB_PROGRAM_INFO_1,
                        DAB_PROGRAM_INFO_2);
    }

    @Test
    public void onProgramListUpdated_withIdsRemoved_invokesMockedCallbacks() throws Exception {
    public void onProgramListUpdated_withFmIdsRemoved_invokesCallbacks() throws Exception {
        UniqueProgramIdentifier fmUniqueId = new UniqueProgramIdentifier(FM_IDENTIFIER);
        ProgramList.Chunk fmRemovedChunk = new ProgramList.Chunk(/* purge= */ false,
                /* complete= */ false, new ArraySet<>(), Set.of(FM_IDENTIFIER));
                /* complete= */ false, new ArraySet<>(), Set.of(fmUniqueId));
        createRadioTuner();
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
        registerListCallbacks(/* numCallbacks= */ 1);
        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        mTunerCallback.onProgramListUpdated(fmRemovedChunk);

        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(FM_IDENTIFIER);
        assertWithMessage("Program info in program list after removing FM id")
                .that(mProgramList.toList()).containsExactly(RDS_PROGRAM_INFO);
        assertWithMessage("Program info FM identifier")
                .that(mProgramList.get(RDS_IDENTIFIER)).isEqualTo(RDS_PROGRAM_INFO);
                .that(mProgramList.toList()).containsExactly(DAB_PROGRAM_INFO_1,
                        DAB_PROGRAM_INFO_2);
    }

    @Test
    public void onProgramListUpdated_withPartOfDabIdsRemoved_doesNotInvokeCallbacks()
            throws Exception {
        ProgramList.Chunk dabRemovedChunk1 = new ProgramList.Chunk(/* purge= */ false,
                /* complete= */ false, new ArraySet<>(), Set.of(DAB_UNIQUE_IDENTIFIER_1));
        createRadioTuner();
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
        registerListCallbacks(/* numCallbacks= */ 1);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        mTunerCallback.onProgramListUpdated(dabRemovedChunk1);

        verify(mListCallbackMocks[0], after(TIMEOUT_MS).never()).onItemRemoved(
                DAB_DMB_SID_EXT_IDENTIFIER);
        assertWithMessage("Program info in program list after removing part of DAB ids")
                .that(mProgramList.toList()).containsExactly(FM_PROGRAM_INFO, DAB_PROGRAM_INFO_2);
    }

    @Test
    public void onProgramListUpdated_withAllDabIdsRemoved_invokesCallbacks()
            throws Exception {
        ProgramList.Chunk dabRemovedChunk1 = new ProgramList.Chunk(/* purge= */ false,
                /* complete= */ false, new ArraySet<>(), Set.of(DAB_UNIQUE_IDENTIFIER_1));
        ProgramList.Chunk dabRemovedChunk2 = new ProgramList.Chunk(/* purge= */ false,
                /* complete= */ false, new ArraySet<>(), Set.of(DAB_UNIQUE_IDENTIFIER_2));
        createRadioTuner();
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
        registerListCallbacks(/* numCallbacks= */ 1);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(dabRemovedChunk1);
        verify(mListCallbackMocks[0], after(TIMEOUT_MS).never()).onItemRemoved(
                DAB_DMB_SID_EXT_IDENTIFIER);

        mTunerCallback.onProgramListUpdated(dabRemovedChunk2);

        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(DAB_DMB_SID_EXT_IDENTIFIER);
        assertWithMessage("Program info in program list after removing all DAB ids")
                .that(mProgramList.toList()).containsExactly(FM_PROGRAM_INFO);
    }

    @Test
@@ -388,18 +442,18 @@ public final class ProgramListTest {
        createRadioTuner();
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
        registerListCallbacks(/* numCallbacks= */ 1);
        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        mTunerCallback.onProgramListUpdated(purgeChunk);

        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(FM_IDENTIFIER);
        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(RDS_IDENTIFIER);
        verify(mListCallbackMocks[0], CALLBACK_TIMEOUT).onItemRemoved(DAB_DMB_SID_EXT_IDENTIFIER);
        assertWithMessage("Program list after purge chunk applied")
                .that(mProgramList.toList()).isEmpty();
    }

    @Test
    public void onProgramListUpdated_afterProgramListClosed_notInvokeMockedCallbacks()
    public void onProgramListUpdated_afterProgramListClosed_notInvokeCallbacks()
            throws Exception {
        createRadioTuner();
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
@@ -407,7 +461,7 @@ public final class ProgramListTest {
        addOnCompleteListeners(/* numListeners= */ 1);
        mProgramList.close();

        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        verify(mListCallbackMocks[0], after(TIMEOUT_MS).never()).onItemChanged(any());
        verify(mListCallbackMocks[0], never()).onItemChanged(any());
@@ -462,7 +516,7 @@ public final class ProgramListTest {
            throws Exception {
        createRadioTuner();
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        mTunerCallback.onBackgroundScanComplete();

@@ -487,7 +541,7 @@ public final class ProgramListTest {
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);

        mTunerCallback.onBackgroundScanComplete();
        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        verify(mTunerCallbackMock, CALLBACK_TIMEOUT).onBackgroundScanComplete();
    }
@@ -512,7 +566,7 @@ public final class ProgramListTest {
                mock(ProgramList.OnCompleteListener.class);

        mProgramList.addOnCompleteListener(mExecutor, onCompleteListenerMock);
        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        verify(onCompleteListenerMock, CALLBACK_TIMEOUT).onComplete();
    }
@@ -524,7 +578,7 @@ public final class ProgramListTest {
        mProgramList = mRadioTuner.getDynamicProgramList(TEST_FILTER);
        addOnCompleteListeners(numListeners);

        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        for (int index = 0; index < numListeners; index++) {
            verify(mOnCompleteListenerMocks[index], CALLBACK_TIMEOUT).onComplete();
@@ -538,7 +592,7 @@ public final class ProgramListTest {
        addOnCompleteListeners(/* numListeners= */ 1);

        mProgramList.removeOnCompleteListener(mOnCompleteListenerMocks[0]);
        mTunerCallback.onProgramListUpdated(FM_RDS_ADD_CHUNK);
        mTunerCallback.onProgramListUpdated(FM_DAB_ADD_CHUNK);

        verify(mOnCompleteListenerMocks[0], after(TIMEOUT_MS).never()).onComplete();
    }
@@ -566,6 +620,13 @@ public final class ProgramListTest {
                /* vendorInfo= */ null);
    }

    private static RadioManager.ProgramInfo createDabProgramInfo(ProgramSelector selector) {
        return new RadioManager.ProgramInfo(selector, selector.getPrimaryId(),
                DAB_ENSEMBLE_IDENTIFIER, /* relatedContents= */ null, /* infoFlags= */ 0,
                /* signalQuality= */ 1, new RadioMetadata.Builder().build(),
                /* vendorInfo= */ null);
    }

    private void createRadioTuner() throws Exception {
        mApplicationInfo.targetSdkVersion = TEST_TARGET_SDK_VERSION;
        when(mContextMock.getApplicationInfo()).thenReturn(mApplicationInfo);
+3 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
import android.hardware.radio.UniqueProgramIdentifier;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -149,12 +150,12 @@ final class AidlTestUtils {

    static ProgramList.Chunk makeChunk(boolean purge, boolean complete,
            List<RadioManager.ProgramInfo> modified,
            List<ProgramSelector.Identifier> removed) throws RemoteException {
            List<UniqueProgramIdentifier> removed) throws RemoteException {
        ArraySet<RadioManager.ProgramInfo> modifiedSet = new ArraySet<>();
        if (modified != null) {
            modifiedSet.addAll(modified);
        }
        ArraySet<ProgramSelector.Identifier> removedSet = new ArraySet<>();
        ArraySet<UniqueProgramIdentifier> removedSet = new ArraySet<>();
        if (removed != null) {
            removedSet.addAll(removed);
        }
+10 −62

File changed.

Preview size limit exceeded, changes collapsed.

+43 −18

File changed.

Preview size limit exceeded, changes collapsed.

Loading