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

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

Merge changes from topic "radio_alert_api" into main

* changes:
  Add radio alert in program info constructor
  Define emergency alert system API
parents d7e89209 5b1d97a1
Loading
Loading
Loading
Loading
+87 −0
Original line number Diff line number Diff line
@@ -6528,6 +6528,92 @@ package android.hardware.radio {
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector.Identifier> CREATOR;
  }
  @FlaggedApi("android.hardware.radio.hd_radio_emergency_alert_system") public final class RadioAlert implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public java.util.List<android.hardware.radio.RadioAlert.AlertInfo> getInfoList();
    method public int getMessageType();
    method public int getStatus();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field public static final int CATEGORY_CBRNE = 10; // 0xa
    field public static final int CATEGORY_ENV = 7; // 0x7
    field public static final int CATEGORY_FIRE = 5; // 0x5
    field public static final int CATEGORY_GEO = 0; // 0x0
    field public static final int CATEGORY_HEALTH = 6; // 0x6
    field public static final int CATEGORY_INFRA = 9; // 0x9
    field public static final int CATEGORY_MET = 1; // 0x1
    field public static final int CATEGORY_OTHER = 11; // 0xb
    field public static final int CATEGORY_RESCUE = 4; // 0x4
    field public static final int CATEGORY_SAFETY = 2; // 0x2
    field public static final int CATEGORY_SECURITY = 3; // 0x3
    field public static final int CATEGORY_TRANSPORT = 8; // 0x8
    field public static final int CERTAINTY_LIKELY = 1; // 0x1
    field public static final int CERTAINTY_OBSERVED = 0; // 0x0
    field public static final int CERTAINTY_POSSIBLE = 2; // 0x2
    field public static final int CERTAINTY_UNKNOWN = 4; // 0x4
    field public static final int CERTAINTY_UNLIKELY = 3; // 0x3
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert> CREATOR;
    field public static final int MESSAGE_TYPE_ALERT = 0; // 0x0
    field public static final int MESSAGE_TYPE_CANCEL = 2; // 0x2
    field public static final int MESSAGE_TYPE_UPDATE = 1; // 0x1
    field public static final int SEVERITY_EXTREME = 0; // 0x0
    field public static final int SEVERITY_MINOR = 3; // 0x3
    field public static final int SEVERITY_MODERATE = 2; // 0x2
    field public static final int SEVERITY_SEVERE = 1; // 0x1
    field public static final int SEVERITY_UNKNOWN = 4; // 0x4
    field public static final int STATUS_ACTUAL = 0; // 0x0
    field public static final int STATUS_EXERCISE = 1; // 0x1
    field public static final int STATUS_TEST = 2; // 0x2
    field public static final int URGENCY_EXPECTED = 1; // 0x1
    field public static final int URGENCY_FUTURE = 2; // 0x2
    field public static final int URGENCY_IMMEDIATE = 0; // 0x0
    field public static final int URGENCY_PAST = 3; // 0x3
    field public static final int URGENCY_UNKNOWN = 4; // 0x4
  }
  public static final class RadioAlert.AlertArea implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public java.util.List<android.hardware.radio.RadioAlert.Geocode> getGeocodes();
    method @NonNull public java.util.List<android.hardware.radio.RadioAlert.Polygon> getPolygons();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.AlertArea> CREATOR;
  }
  public static final class RadioAlert.AlertInfo implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public java.util.List<android.hardware.radio.RadioAlert.AlertArea> getAreas();
    method @NonNull public int[] getCategories();
    method public int getCertainty();
    method @NonNull public String getDescription();
    method @Nullable public String getLanguage();
    method public int getSeverity();
    method public int getUrgency();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.AlertInfo> CREATOR;
  }
  public static final class RadioAlert.Coordinate implements android.os.Parcelable {
    method public int describeContents();
    method @FloatRange(from=-90.0, to=90.0) public double getLatitude();
    method @FloatRange(from=-180.0, to=180.0) public double getLongitude();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.Coordinate> CREATOR;
  }
  public static final class RadioAlert.Geocode implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public String getValue();
    method @NonNull public String getValueName();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.Geocode> CREATOR;
  }
  public static final class RadioAlert.Polygon implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public java.util.List<android.hardware.radio.RadioAlert.Coordinate> getCoordinates();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.radio.RadioAlert.Polygon> CREATOR;
  }
  public class RadioManager {
    method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public void addAnnouncementListener(@NonNull java.util.Set<java.lang.Integer>, @NonNull android.hardware.radio.Announcement.OnListUpdatedListener);
    method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public void addAnnouncementListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.Set<java.lang.Integer>, @NonNull android.hardware.radio.Announcement.OnListUpdatedListener);
@@ -6663,6 +6749,7 @@ package android.hardware.radio {
  public static class RadioManager.ProgramInfo implements android.os.Parcelable {
    method public int describeContents();
    method @FlaggedApi("android.hardware.radio.hd_radio_emergency_alert_system") @Nullable public android.hardware.radio.RadioAlert getAlert();
    method @Deprecated public int getChannel();
    method @Nullable public android.hardware.radio.ProgramSelector.Identifier getLogicallyTunedTo();
    method public android.hardware.radio.RadioMetadata getMetadata();
+497 −24

File changed.

Preview size limit exceeded, changes collapsed.

+69 −4
Original line number Diff line number Diff line
@@ -1547,6 +1547,7 @@ public class RadioManager {
        private final int mSignalQuality;
        @Nullable private final RadioMetadata mMetadata;
        @NonNull private final Map<String, String> mVendorInfo;
        @Nullable private final RadioAlert mAlert;

        /** @hide */
        public ProgramInfo(@NonNull ProgramSelector selector,
@@ -1555,6 +1556,30 @@ public class RadioManager {
                @Nullable Collection<ProgramSelector.Identifier> relatedContent,
                int infoFlags, int signalQuality, @Nullable RadioMetadata metadata,
                @Nullable Map<String, String> vendorInfo) {
            this(selector, logicallyTunedTo, physicallyTunedTo, relatedContent, infoFlags,
                    signalQuality, metadata, vendorInfo, /* alert= */ null);
        }

        /**
         * Constructor for program info.
         *
         * @param selector Program selector
         * @param logicallyTunedTo Program identifier logically tuned to
         * @param physicallyTunedTo Program identifier physically tuned to
         * @param relatedContent Related content
         * @param infoFlags Program info flags
         * @param signalQuality Signal quality
         * @param metadata Radio metadata
         * @param vendorInfo Vendor parameters
         * @param alert Radio alert
         * @hide
         */
        public ProgramInfo(@NonNull ProgramSelector selector,
                @Nullable ProgramSelector.Identifier logicallyTunedTo,
                @Nullable ProgramSelector.Identifier physicallyTunedTo,
                @Nullable Collection<ProgramSelector.Identifier> relatedContent,
                int infoFlags, int signalQuality, @Nullable RadioMetadata metadata,
                @Nullable Map<String, String> vendorInfo, @Nullable RadioAlert alert) {
            mSelector = Objects.requireNonNull(selector);
            mLogicallyTunedTo = logicallyTunedTo;
            mPhysicallyTunedTo = physicallyTunedTo;
@@ -1568,6 +1593,7 @@ public class RadioManager {
            mSignalQuality = signalQuality;
            mMetadata = metadata;
            mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
            mAlert = alert;
        }

        /**
@@ -1745,6 +1771,19 @@ public class RadioManager {
            return (mInfoFlags & FLAG_HD_AUDIO_ACQUIRED) != 0;
        }

        /**
         * Get alert message.
         *
         * <p>Alert message can be sent from a radio station of technologies such as HD radio to
         * the radio users for some emergency events.
         *
         * @return alert message if it exists, otherwise {@code null}
         */
        @FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
        @Nullable public RadioAlert getAlert() {
            return mAlert;
        }

        /**
         * Signal quality (as opposed to the name) indication from 0 (no signal)
         * to 100 (excellent)
@@ -1786,6 +1825,12 @@ public class RadioManager {
            mSignalQuality = in.readInt();
            mMetadata = in.readTypedObject(RadioMetadata.CREATOR);
            mVendorInfo = Utils.readStringMap(in);
            if (Flags.hdRadioEmergencyAlertSystem()) {
                boolean hasNonNullAlert = in.readBoolean();
                mAlert = hasNonNullAlert ? in.readTypedObject(RadioAlert.CREATOR) : null;
            } else {
                mAlert = null;
            }
        }

        public static final @android.annotation.NonNull Parcelable.Creator<ProgramInfo> CREATOR
@@ -1809,6 +1854,14 @@ public class RadioManager {
            dest.writeInt(mSignalQuality);
            dest.writeTypedObject(mMetadata, flags);
            Utils.writeStringMap(dest, mVendorInfo);
            if (Flags.hdRadioEmergencyAlertSystem()) {
                if (mAlert == null) {
                    dest.writeBoolean(false);
                } else {
                    dest.writeBoolean(true);
                    dest.writeTypedObject(mAlert, flags);
                }
            }
        }

        @Override
@@ -1819,19 +1872,28 @@ public class RadioManager {
        @NonNull
        @Override
        public String toString() {
            return "ProgramInfo"
            String prorgamInfoString = "ProgramInfo"
                    + " [selector=" + mSelector
                    + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo)
                    + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo)
                    + ", relatedContent=" + mRelatedContent.size()
                    + ", infoFlags=" + mInfoFlags
                    + ", mSignalQuality=" + mSignalQuality
                    + ", mMetadata=" + Objects.toString(mMetadata)
                    + "]";
                    + ", signalQuality=" + mSignalQuality
                    + ", metadata=" + Objects.toString(mMetadata);
            if (Flags.hdRadioEmergencyAlertSystem()) {
                prorgamInfoString += ", alert=" + Objects.toString(mAlert);
            }
            prorgamInfoString += "]";
            return prorgamInfoString;
        }

        @Override
        public int hashCode() {
            if (Flags.hdRadioEmergencyAlertSystem()) {
                return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo,
                        mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo,
                        mAlert);
            }
            return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo,
                mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo);
        }
@@ -1851,6 +1913,9 @@ public class RadioManager {
            if (!Objects.equals(mMetadata, other.mMetadata)) return false;
            if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;

            if (Flags.hdRadioEmergencyAlertSystem()) {
                return Objects.equals(mAlert, other.mAlert);
            }
            return true;
        }
    }
+104 −3
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;

@@ -142,10 +144,13 @@ public final class RadioManagerTest {
            new ProgramSelector.Identifier[]{}, /* vendorIds= */ null);

    private static final RadioMetadata METADATA = createMetadata();
    private static final RadioAlert HD_ALERT = createRadioAlert();
    private static final RadioManager.ProgramInfo DAB_PROGRAM_INFO =
            createDabProgramInfo(DAB_SELECTOR);
    private static final RadioManager.ProgramInfo HD_PROGRAM_INFO = createHdProgramInfo(
            HD_SELECTOR);
            HD_SELECTOR, /* alert= */ null);
    private static final RadioManager.ProgramInfo HD_PROGRAM_INFO_WITH_ALERT = createHdProgramInfo(
            HD_SELECTOR, HD_ALERT);

    private static final int EVENT_ANNOUNCEMENT_TYPE = Announcement.TYPE_EVENT;
    private static final List<Announcement> TEST_ANNOUNCEMENT_LIST = Arrays.asList(
@@ -1162,6 +1167,20 @@ public final class RadioManagerTest {
                .that(DAB_PROGRAM_INFO.getVendorInfo()).isEmpty();
    }

    @Test
    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void getAlert() {
        mExpect.withMessage("Alert in HD program info")
                .that(HD_PROGRAM_INFO_WITH_ALERT.getAlert()).isEqualTo(HD_ALERT);
    }

    @Test
    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void getAlert_withNullAlert() {
        mExpect.withMessage("Null alert in HD program info")
                .that(HD_PROGRAM_INFO.getAlert()).isNull();
    }

    @Test
    public void describeContents_forProgramInfo() {
        mExpect.withMessage("Program info contents")
@@ -1189,6 +1208,69 @@ public final class RadioManagerTest {
                .that(programInfoFromParcel).isEqualTo(DAB_PROGRAM_INFO);
    }

    @Test
    @DisableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void equals_forProgramInfoWithAlertAndFlagDisabled() {
        mExpect.withMessage("Program info with alert and flag disabled")
                .that(HD_PROGRAM_INFO_WITH_ALERT).isEqualTo(HD_PROGRAM_INFO);
    }

    @Test
    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void equals_forProgramInfoWithAlertAndFlagEnabled() {
        RadioManager.ProgramInfo sameProgramInfoWithAlert = createHdProgramInfo(HD_SELECTOR,
                HD_ALERT);

        mExpect.withMessage("Program info with alert and flag enabled")
                .that(HD_PROGRAM_INFO_WITH_ALERT).isEqualTo(sameProgramInfoWithAlert);
    }

    @Test
    @DisableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void hashcode_forProgramInfoWithAlertAndFlagDisabled() {
        mExpect.withMessage("Hash code of program info with alert and flag disabled")
                .that(HD_PROGRAM_INFO_WITH_ALERT.hashCode()).isEqualTo(HD_PROGRAM_INFO.hashCode());
    }

    @Test
    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void hashcode_forProgramInfoWithAlertAndFlagEnabled() {
        RadioManager.ProgramInfo sameProgramInfoWithAlert = createHdProgramInfo(HD_SELECTOR,
                HD_ALERT);

        mExpect.withMessage("Hash code of program info with alert and flag enabled")
                .that(HD_PROGRAM_INFO_WITH_ALERT.hashCode())
                .isEqualTo(sameProgramInfoWithAlert.hashCode());
    }

    @Test
    @DisableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void writeToParcel_forProgramInfoWithAlertAndFlagDisabled() {
        Parcel parcel = Parcel.obtain();

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

        RadioManager.ProgramInfo programInfoFromParcel =
                RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel);
        mExpect.withMessage("Program info created from parcel with alert and flag disabled")
                .that(programInfoFromParcel).isEqualTo(HD_PROGRAM_INFO);
    }

    @Test
    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void writeToParcel_forProgramInfoWithAlertAndFlagEnabled() {
        Parcel parcel = Parcel.obtain();

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

        RadioManager.ProgramInfo programInfoFromParcel =
                RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel);
        mExpect.withMessage("Program info created from parcel with alert and flag enabled")
                .that(programInfoFromParcel).isEqualTo(HD_PROGRAM_INFO_WITH_ALERT);
    }

    @Test
    public void equals_withSameProgramInfo_returnsTrue() {
        RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(DAB_SELECTOR);
@@ -1209,6 +1291,13 @@ public final class RadioManagerTest {
                .that(DAB_PROGRAM_INFO).isNotEqualTo(dabProgramInfoCompared);
    }

    @Test
    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
    public void equals_withDifferentAlert_returnsFalse() {
        mExpect.withMessage("Program info with different alerts")
                .that(HD_PROGRAM_INFO).isNotEqualTo(HD_PROGRAM_INFO_WITH_ALERT);
    }

    @Test
    public void listModules_forRadioManager() throws Exception {
        createRadioManager();
@@ -1399,19 +1488,31 @@ public final class RadioManagerTest {
        return metadataBuilder.putString(RadioMetadata.METADATA_KEY_ARTIST, "artistTest").build();
    }

    private static RadioAlert createRadioAlert() {
        RadioAlert.AlertArea alertArea = new RadioAlert.AlertArea(new ArrayList<>(),
                List.of(new RadioAlert.Geocode("SAME", "006109")));
        RadioAlert.AlertInfo alertInfo = new RadioAlert.AlertInfo(
                new int[]{RadioAlert.CATEGORY_FIRE}, RadioAlert.URGENCY_EXPECTED,
                RadioAlert.SEVERITY_MODERATE, RadioAlert.CERTAINTY_OBSERVED,
                "alert description", List.of(alertArea), "en-US");
        return new RadioAlert(RadioAlert.STATUS_ACTUAL, RadioAlert.MESSAGE_TYPE_ALERT,
                List.of(alertInfo));
    }

    private static RadioManager.ProgramInfo createDabProgramInfo(ProgramSelector selector) {
        return new RadioManager.ProgramInfo(selector, selector.getPrimaryId(),
                DAB_FREQUENCY_IDENTIFIER, Arrays.asList(DAB_SID_EXT_IDENTIFIER_RELATED),
                INFO_FLAGS_DAB, SIGNAL_QUALITY, METADATA, /* vendorInfo= */ null);
    }

    private static RadioManager.ProgramInfo createHdProgramInfo(ProgramSelector selector) {
    private static RadioManager.ProgramInfo createHdProgramInfo(ProgramSelector selector,
            RadioAlert alert) {
        long frequency = (selector.getPrimaryId().getValue() >> 32);
        ProgramSelector.Identifier physicallyTunedToId = new ProgramSelector.Identifier(
                ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, frequency);
        return new RadioManager.ProgramInfo(selector, selector.getPrimaryId(), physicallyTunedToId,
                Collections.emptyList(), INFO_FLAGS_HD, SIGNAL_QUALITY, METADATA,
                /* vendorInfo= */ null);
                /* vendorInfo= */ null, alert);
    }

    private void createRadioManager() throws RemoteException {