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

Commit c450458d authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add unique program identifier for broadcast radio" into main

parents d44ce875 45c6f09e
Loading
Loading
Loading
Loading
+20 −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.hardware.radio;

/** @hide */
parcelable UniqueProgramIdentifier;
+163 −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.hardware.radio;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * Identifier that can uniquely identifies a program.
 *
 * This is a transport class used for internal communication between
 * Broadcast Radio Service and Radio Manager. Do not use it directly.
 *
 * @hide
 */
public final class UniqueProgramIdentifier implements Parcelable {

    @NonNull private final ProgramSelector.Identifier mPrimaryId;
    @NonNull private final ProgramSelector.Identifier[] mCriticalSecondaryIds;

    /**
     * Check whether some secondary identifier is needed to uniquely specify a program for
     * a given primary identifier type
     *
     * @param type primary identifier type {@link ProgramSelector.IdentifierType}
     * @return whether some secondary identifier is needed to uniquely specify a program.
     */
    public static boolean requireCriticalSecondaryIds(@ProgramSelector.IdentifierType int type) {
        return type == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT || type
                == ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT;
    }

    public UniqueProgramIdentifier(ProgramSelector selector) {
        Objects.requireNonNull(selector, "Program selector can not be null");
        mPrimaryId = selector.getPrimaryId();
        switch (mPrimaryId.getType()) {
            case ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT:
            case ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT:
                ProgramSelector.Identifier ensembleId = null;
                ProgramSelector.Identifier frequencyId = null;
                ProgramSelector.Identifier[] secondaryIds = selector.getSecondaryIds();
                for (int i = 0; i < secondaryIds.length; i++) {
                    if (ensembleId == null && secondaryIds[i].getType()
                            == ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE) {
                        ensembleId = selector.getSecondaryIds()[i];
                    } else if (frequencyId == null && secondaryIds[i].getType()
                            == ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY) {
                        frequencyId = secondaryIds[i];
                    }
                    if (ensembleId != null && frequencyId != null) {
                        break;
                    }
                }
                if (ensembleId == null) {
                    if (frequencyId == null) {
                        mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
                    } else {
                        mCriticalSecondaryIds = new ProgramSelector.Identifier[]{frequencyId};
                    }
                } else if (frequencyId == null) {
                    mCriticalSecondaryIds = new ProgramSelector.Identifier[]{ensembleId};
                } else {
                    mCriticalSecondaryIds = new ProgramSelector.Identifier[]{ensembleId,
                            frequencyId};
                }
                break;
            default:
                mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
        }

    }

    public UniqueProgramIdentifier(ProgramSelector.Identifier primaryId) {
        mPrimaryId = primaryId;
        mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
    }

    @NonNull
    public ProgramSelector.Identifier getPrimaryId() {
        return mPrimaryId;
    }

    @NonNull
    public List<ProgramSelector.Identifier> getCriticalSecondaryIds() {
        return List.of(mCriticalSecondaryIds);
    }

    @NonNull
    @Override
    public String toString() {
        return new StringBuilder("UniqueProgramIdentifier(primary=").append(mPrimaryId)
                .append(", criticalSecondary=")
                .append(Arrays.toString(mCriticalSecondaryIds)).append(")")
                .toString();
    }

    @Override
    public int hashCode() {
        return Objects.hash(mPrimaryId, Arrays.hashCode(mCriticalSecondaryIds));
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof UniqueProgramIdentifier)) return false;
        UniqueProgramIdentifier other = (UniqueProgramIdentifier) obj;
        return other.mPrimaryId.equals(mPrimaryId)
                && Arrays.equals(other.mCriticalSecondaryIds, mCriticalSecondaryIds);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    private UniqueProgramIdentifier(Parcel in) {
        mPrimaryId = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
        mCriticalSecondaryIds = in.createTypedArray(ProgramSelector.Identifier.CREATOR);
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeTypedObject(mPrimaryId, 0);
        dest.writeTypedArray(mCriticalSecondaryIds, 0);
        if (Stream.of(mCriticalSecondaryIds).anyMatch(Objects::isNull)) {
            throw new IllegalArgumentException(
                    "criticalSecondaryIds list must not contain nulls");
        }
    }

    @NonNull
    public static final Parcelable.Creator<UniqueProgramIdentifier> CREATOR =
            new Parcelable.Creator<UniqueProgramIdentifier>() {
                public UniqueProgramIdentifier createFromParcel(Parcel in) {
                    return new UniqueProgramIdentifier(in);
                }

                public UniqueProgramIdentifier[] newArray(int size) {
                    return new UniqueProgramIdentifier[size];
                }
            };
}
+2 −2
Original line number Diff line number Diff line
@@ -437,8 +437,8 @@ public final class ProgramSelectorTest {

    @Test
    public void writeToParcel_forProgramSelector() {
        ProgramSelector selectorExpected =
                getFmSelector(/* secondaryIds= */ null, /* vendorIds= */ null);
        ProgramSelector selectorExpected = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
        Parcel parcel = Parcel.obtain();

        selectorExpected.writeToParcel(parcel, /* flags= */ 0);
+187 −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.hardware.radio;

import android.annotation.Nullable;
import android.os.Parcel;

import com.google.common.truth.Expect;

import org.junit.Rule;
import org.junit.Test;

public final class UniqueProgramIdentifierTest {
    private static final ProgramSelector.Identifier FM_IDENTIFIER = new ProgramSelector.Identifier(
            ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, /* value= */ 88_500);

    private static final ProgramSelector.Identifier DAB_DMB_SID_EXT_IDENTIFIER_1 =
            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
                    /* value= */ 0xA000000111L);
    private static final ProgramSelector.Identifier DAB_ENSEMBLE_IDENTIFIER =
            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
                    /* value= */ 0x1001);
    private static final ProgramSelector.Identifier DAB_FREQUENCY_IDENTIFIER =
            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
                    /* value= */ 220352);
    private static final ProgramSelector.Identifier DAB_SCID_IDENTIFIER =
            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SCID,
                    /* value= */ 0x101);

    @Rule
    public final Expect expect = Expect.create();

    @Test
    public void getPrimaryId_forUniqueProgramIdentifier() {
        ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
        UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);

        expect.withMessage("Primary id of DAB unique identifier")
                .that(dabIdentifier.getPrimaryId()).isEqualTo(DAB_DMB_SID_EXT_IDENTIFIER_1);
    }

    @Test
    public void getCriticalSecondaryIds_forDabUniqueProgramIdentifier() {
        ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER, DAB_SCID_IDENTIFIER},
                /* vendorIds= */ null);
        UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);

        expect.withMessage("Critical secondary ids of DAB unique identifier")
                .that(dabIdentifier.getCriticalSecondaryIds()).containsExactly(
                        DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER);
    }

    @Test
    public void getCriticalSecondaryIds_forFmUniqueProgramIdentifier() {
        UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(
                new ProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, FM_IDENTIFIER,
                        new ProgramSelector.Identifier[]{new ProgramSelector.Identifier(
                                ProgramSelector.IDENTIFIER_TYPE_RDS_PI, /* value= */ 0x1003)},
                        /* vendorIds= */ null));

        expect.withMessage("Empty critical secondary id list of FM unique identifier")
                .that(fmUniqueIdentifier.getCriticalSecondaryIds()).isEmpty();
    }

    @Test
    public void toString_forUniqueProgramIdentifier() {
        ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
        UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);

        String identifierString = dabIdentifier.toString();

        expect.withMessage("Primary id in DAB unique identifier")
                .that(identifierString).contains(DAB_DMB_SID_EXT_IDENTIFIER_1.toString());
        expect.withMessage("Ensemble id in DAB unique identifier")
                .that(identifierString).contains(DAB_ENSEMBLE_IDENTIFIER.toString());
        expect.withMessage("Frequency id in DAB unique identifier")
                .that(identifierString).contains(DAB_FREQUENCY_IDENTIFIER.toString());
    }

    @Test
    public void hashCode_withTheSameUniqueProgramIdentifier_equals() {
        ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
        ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_FREQUENCY_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER}, /* vendorIds= */ null);
        UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
        UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);

        expect.withMessage("Hash code of the same DAB unique identifiers")
                .that(dabIdentifier1.hashCode()).isEqualTo(dabIdentifier2.hashCode());
    }

    @Test
    public void equals_withIdsForUniqueProgramIdentifier_returnsTrue() {
        ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
        ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_FREQUENCY_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER}, /* vendorIds= */ null);
        UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
        UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);

        expect.withMessage("The same DAB unique identifiers")
                .that(dabIdentifier1).isEqualTo(dabIdentifier2);
    }

    @Test
    public void equals_withDifferentPrimaryIdsForUniqueProgramIdentifier_returnsFalse() {
        ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
        UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
        UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(FM_IDENTIFIER);

        expect.withMessage("Unique identifier with different primary ids")
                .that(dabIdentifier1).isNotEqualTo(fmUniqueIdentifier);
    }

    @Test
    public void equals_withDifferentSecondaryIdsForUniqueProgramIdentifier_returnsFalse() {
        ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
        ProgramSelector.Identifier dabFreqIdentifier2 = new ProgramSelector.Identifier(
                ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, /* value= */ 222064);
        ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, dabFreqIdentifier2}, /* vendorIds= */ null);
        UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
        UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);

        expect.withMessage("DAB unique identifier with different secondary ids")
                .that(dabIdentifier1).isNotEqualTo(dabIdentifier2);
    }

    @Test
    public void describeContents_forUniqueProgramIdentifier() {
        UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(FM_IDENTIFIER);

        expect.withMessage("FM unique identifier contents")
                .that(fmUniqueIdentifier.describeContents()).isEqualTo(0);
    }

    @Test
    public void newArray_forUniqueProgramIdentifier() {
        int createArraySize = 3;
        UniqueProgramIdentifier[] identifiers = UniqueProgramIdentifier.CREATOR.newArray(
                createArraySize);

        expect.withMessage("Unique identifiers").that(identifiers).hasLength(createArraySize);
    }

    @Test
    public void writeToParcel_forUniqueProgramIdentifier() {
        ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
        UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
        Parcel parcel = Parcel.obtain();

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

        UniqueProgramIdentifier identifierFromParcel = UniqueProgramIdentifier.CREATOR
                .createFromParcel(parcel);
        expect.withMessage("Unique identifier created from parcel")
                .that(identifierFromParcel).isEqualTo(dabIdentifier);
    }

    private ProgramSelector getDabSelector(@Nullable ProgramSelector.Identifier[] secondaryIds,
            @Nullable long[] vendorIds) {
        return new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, DAB_DMB_SID_EXT_IDENTIFIER_1,
                secondaryIds, vendorIds);
    }
}