From 81f50685cbc558f67039af1a7de03014eca2b216 Mon Sep 17 00:00:00 2001 From: Weilin Xu Date: Thu, 24 Nov 2022 00:25:37 +0000 Subject: [PATCH 1/3] Update RadioTuner APIs for AIDL HAL migration APIs in RadioTuner were updated to adopt broadcast radio HAL migration to AIDL: - New APIs seek and onConfigFlagUpdated were added since corresponding new interface methods were introduced in broadcast radio AIDL HAL. - APIs only used in HIDL 1.x broadcast radio HAL were deprecated. - The use of deprecated onError callback was replaced by onTuneFailed in tune, seek, scan and step APIs. Corresponding result types were also defined in RadioTuner. - Multi-user behaviors for RadioTuner APIs were specified. Meanwhile, a new DAB radio primary identifier was added, which was extended to 44 bits to support Digital Multimedia Broadcasting (DMB) radio. The integer annotations in radio APIs were also improved. Bug: 257337458 Test: atest com.android.server.broadcastradio Test: atest android.hardware.radio.tests.unittests Test: atest android.broadcastradio.cts Change-Id: I46f0bdfae238de072e46f6561fa3f17c73dd45c8 --- core/api/system-current.txt | 65 ++-- core/java/android/hardware/radio/ITuner.aidl | 2 +- .../hardware/radio/ITunerCallback.aidl | 19 + .../hardware/radio/ProgramSelector.java | 32 +- .../android/hardware/radio/RadioManager.java | 20 ++ .../android/hardware/radio/RadioTuner.java | 324 ++++++++++++++---- .../android/hardware/radio/TunerAdapter.java | 17 +- .../hardware/radio/TunerCallbackAdapter.java | 5 + .../tests/functional/RadioTunerTest.java | 5 +- .../unittests/DefaultRadioTunerTest.java | 40 ++- .../tests/unittests/TunerAdapterTest.java | 38 +- .../broadcastradio/aidl/TunerSessionTest.java | 37 +- .../hal2/TunerSessionHidlTest.java | 20 +- .../broadcastradio/aidl/ConversionUtils.java | 1 + .../broadcastradio/aidl/RadioModule.java | 5 +- .../broadcastradio/aidl/TunerSession.java | 9 +- .../server/broadcastradio/hal1/Tuner.java | 4 +- .../broadcastradio/hal1/TunerCallback.java | 7 +- .../server/broadcastradio/hal2/Convert.java | 1 + .../broadcastradio/hal2/TunerSession.java | 8 +- 20 files changed, 510 insertions(+), 149 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index e4b71378cef32..6e40f083fa008 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5267,11 +5267,12 @@ package android.hardware.radio { method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; field public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1; // 0x1 + field public static final int IDENTIFIER_TYPE_DAB_DMB_SID_EXT = 14; // 0xe field public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6; // 0x6 field public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8; // 0x8 field public static final int IDENTIFIER_TYPE_DAB_SCID = 7; // 0x7 - field public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5 - field public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5 + field @Deprecated public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5 + field @Deprecated public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5 field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa field @Deprecated public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9 @@ -5308,7 +5309,7 @@ package android.hardware.radio { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } - @IntDef(prefix={"IDENTIFIER_TYPE_"}, value={android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_INVALID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_RDS_PI, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_NAME, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SCID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_MODULATION, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL}) @IntRange(from=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.IdentifierType { + @IntDef(prefix={"IDENTIFIER_TYPE_"}, value={android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_INVALID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_RDS_PI, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_HD_STATION_NAME, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_SCID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DRMO_MODULATION, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT}) @IntRange(from=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.IDENTIFIER_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.IdentifierType { } @Deprecated @IntDef(prefix={"PROGRAM_TYPE_"}, value={android.hardware.radio.ProgramSelector.PROGRAM_TYPE_INVALID, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_AM, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_FM, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_AM_HD, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_FM_HD, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_DAB, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_DRMO, android.hardware.radio.ProgramSelector.PROGRAM_TYPE_SXM}) @IntRange(from=android.hardware.radio.ProgramSelector.PROGRAM_TYPE_VENDOR_START, to=android.hardware.radio.ProgramSelector.PROGRAM_TYPE_VENDOR_END) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ProgramSelector.ProgramType { @@ -5523,30 +5524,31 @@ package android.hardware.radio { public abstract class RadioTuner { ctor public RadioTuner(); - method public abstract int cancel(); - method public abstract void cancelAnnouncement(); - method public abstract void close(); - method @Deprecated public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]); - method @Nullable public android.hardware.radio.ProgramList getDynamicProgramList(@Nullable android.hardware.radio.ProgramList.Filter); - method public abstract boolean getMute(); - method @NonNull public java.util.Map getParameters(@NonNull java.util.List); - method @Deprecated public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]); - method @Deprecated @NonNull public abstract java.util.List getProgramList(@Nullable java.util.Map); - method public abstract boolean hasControl(); - method @Deprecated public abstract boolean isAnalogForced(); - method @Deprecated public abstract boolean isAntennaConnected(); - method public boolean isConfigFlagSet(int); - method public boolean isConfigFlagSupported(int); - method public abstract int scan(int, boolean); - method @Deprecated public abstract void setAnalogForced(boolean); - method public void setConfigFlag(int, boolean); - method @Deprecated public abstract int setConfiguration(android.hardware.radio.RadioManager.BandConfig); - method public abstract int setMute(boolean); - method @NonNull public java.util.Map setParameters(@NonNull java.util.Map); - method public abstract boolean startBackgroundScan(); - method public abstract int step(int, boolean); - method @Deprecated public abstract int tune(int, int); - method public abstract void tune(@NonNull android.hardware.radio.ProgramSelector); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int cancel(); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void cancelAnnouncement(); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void close(); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public android.hardware.radio.ProgramList getDynamicProgramList(@Nullable android.hardware.radio.ProgramList.Filter); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean getMute(); + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public java.util.Map getParameters(@NonNull java.util.List); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract java.util.List getProgramList(@Nullable java.util.Map); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean hasControl(); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean isAnalogForced(); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean isAntennaConnected(); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public boolean isConfigFlagSet(int); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public boolean isConfigFlagSupported(int); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int scan(int, boolean); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public int seek(int, boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void setAnalogForced(boolean); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public void setConfigFlag(int, boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int setConfiguration(android.hardware.radio.RadioManager.BandConfig); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int setMute(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public java.util.Map setParameters(@NonNull java.util.Map); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean startBackgroundScan(); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int step(int, boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int tune(int, int); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void tune(@NonNull android.hardware.radio.ProgramSelector); field public static final int DIRECTION_DOWN = 1; // 0x1 field public static final int DIRECTION_UP = 0; // 0x0 field @Deprecated public static final int ERROR_BACKGROUND_SCAN_FAILED = 6; // 0x6 @@ -5556,6 +5558,14 @@ package android.hardware.radio { field @Deprecated public static final int ERROR_HARDWARE_FAILURE = 0; // 0x0 field @Deprecated public static final int ERROR_SCAN_TIMEOUT = 3; // 0x3 field @Deprecated public static final int ERROR_SERVER_DIED = 1; // 0x1 + field public static final int TUNER_RESULT_CANCELED = 6; // 0x6 + field public static final int TUNER_RESULT_INTERNAL_ERROR = 1; // 0x1 + field public static final int TUNER_RESULT_INVALID_ARGUMENTS = 2; // 0x2 + field public static final int TUNER_RESULT_INVALID_STATE = 3; // 0x3 + field public static final int TUNER_RESULT_NOT_SUPPORTED = 4; // 0x4 + field public static final int TUNER_RESULT_OK = 0; // 0x0 + field public static final int TUNER_RESULT_TIMEOUT = 5; // 0x5 + field public static final int TUNER_RESULT_UNKNOWN_ERROR = 7; // 0x7 } public abstract static class RadioTuner.Callback { @@ -5563,6 +5573,7 @@ package android.hardware.radio { method public void onAntennaState(boolean); method public void onBackgroundScanAvailabilityChange(boolean); method public void onBackgroundScanComplete(); + method public void onConfigFlagUpdated(int, boolean); method @Deprecated public void onConfigurationChanged(android.hardware.radio.RadioManager.BandConfig); method public void onControlChanged(boolean); method public void onEmergencyAnnouncement(boolean); diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl index 7bf234b9d5a4b..e68c3cccc074c 100644 --- a/core/java/android/hardware/radio/ITuner.aidl +++ b/core/java/android/hardware/radio/ITuner.aidl @@ -49,7 +49,7 @@ interface ITuner { /** * @throws IllegalStateException if called out of sequence */ - void scan(boolean directionDown, boolean skipSubChannel); + void seek(boolean directionDown, boolean skipSubChannel); /** * @throws IllegalArgumentException if invalid arguments are passed diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl index f98947b50e345..13092cc66ccd1 100644 --- a/core/java/android/hardware/radio/ITunerCallback.aidl +++ b/core/java/android/hardware/radio/ITunerCallback.aidl @@ -24,6 +24,13 @@ import android.hardware.radio.RadioMetadata; /** {@hide} */ oneway interface ITunerCallback { void onError(int status); + + /** + * Callback called when tuning operations, such as tune, step, seek, failed. + * + * @param result Tuning result of {@link RadioTuner#TunerResultType} type. + * @param selector Program selector used for the tuning operation. + */ void onTuneFailed(int result, in ProgramSelector selector); void onConfigurationChanged(in RadioManager.BandConfig config); void onCurrentProgramInfoChanged(in RadioManager.ProgramInfo info); @@ -36,6 +43,18 @@ oneway interface ITunerCallback { void onProgramListUpdated(in ProgramList.Chunk chunk); /** + * Callback for passing updates to config flags from {@link IRadioService} to + * {@link RadioTuner}. + * + * @param flag Config flag (defined in {@link RadioManager.ConfigFlag}) updated + * @param value Updated value for the config flag + */ + void onConfigFlagUpdated(int flag, boolean value); + + /** + * Callback for passing updates to vendor-specific parameter values from + * {@link IRadioService} to {@link RadioTuner}. + * * @param parameters Vendor-specific key-value pairs */ void onParametersUpdated(in Map parameters); diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index 8a92135151229..7faa285a8fee9 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -167,7 +167,10 @@ public final class ProgramSelector implements Parcelable { public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; /** * @see {@link IDENTIFIER_TYPE_DAB_SID_EXT} + * + * @deprecated use {@link #IDENTIFIER_TYPE_DAB_DMB_SID_EXT} instead */ + @Deprecated public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; /** * 28bit compound primary identifier for Digital Audio Broadcasting. @@ -183,7 +186,10 @@ public final class ProgramSelector implements Parcelable { * * The remaining bits should be set to zeros when writing on the chip side * and ignored when read. + * + * @deprecated use {@link #IDENTIFIER_TYPE_DAB_DMB_SID_EXT} instead */ + @Deprecated public static final int IDENTIFIER_TYPE_DAB_SID_EXT = IDENTIFIER_TYPE_DAB_SIDECC; /** 16bit */ public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6; @@ -197,7 +203,7 @@ public final class ProgramSelector implements Parcelable { public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; /** * 1: AM, 2:FM - * @deprecated use {@link IDENTIFIER_TYPE_DRMO_FREQUENCY} instead + * @deprecated use {@link #IDENTIFIER_TYPE_DRMO_FREQUENCY} instead */ @Deprecated public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; @@ -205,6 +211,23 @@ public final class ProgramSelector implements Parcelable { public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; /** 0-999 range */ public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; + /** + * 44bit compound primary identifier for Digital Audio Broadcasting and + * Digital Multimedia Broadcasting. + * + *

Consists of (from the LSB): + * - 32bit: SId; + * - 8bit: ECC code; + * - 4bit: SCIdS. + * + *

SCIdS (Service Component Identifier within the Service) value + * of 0 represents the main service, while 1 and above represents + * secondary services. + * + * The remaining bits should be set to zeros when writing on the chip side + * and ignored when read. + */ + public static final int IDENTIFIER_TYPE_DAB_DMB_SID_EXT = 14; /** * Primary identifier for vendor-specific radio technology. * The value format is determined by a vendor. @@ -219,12 +242,12 @@ public final class ProgramSelector implements Parcelable { */ public static final int IDENTIFIER_TYPE_VENDOR_END = PROGRAM_TYPE_VENDOR_END; /** - * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_START} instead + * @deprecated use {@link #IDENTIFIER_TYPE_VENDOR_START} instead */ @Deprecated public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = IDENTIFIER_TYPE_VENDOR_START; /** - * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_END} instead + * @deprecated use {@link #IDENTIFIER_TYPE_VENDOR_END} instead */ @Deprecated public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END; @@ -245,6 +268,7 @@ public final class ProgramSelector implements Parcelable { IDENTIFIER_TYPE_DRMO_MODULATION, IDENTIFIER_TYPE_SXM_SERVICE_ID, IDENTIFIER_TYPE_SXM_CHANNEL, + IDENTIFIER_TYPE_DAB_DMB_SID_EXT, }) @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END) @Retention(RetentionPolicy.SOURCE) @@ -285,7 +309,7 @@ public final class ProgramSelector implements Parcelable { * Type of a radio technology. * * @return program type. - * @deprecated use {@link getPrimaryId} instead + * @deprecated use {@link #getPrimaryId} instead */ @Deprecated public @ProgramType int getProgramType() { diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 9a217f9b5119a..8c6083ce49b63 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -82,6 +82,24 @@ public class RadioManager { /** Method return status: time out before operation completion */ public static final int STATUS_TIMED_OUT = -110; + /** + * Radio operation status types + * + * @hide + */ + @IntDef(prefix = { "STATUS_" }, value = { + STATUS_OK, + STATUS_ERROR, + STATUS_PERMISSION_DENIED, + STATUS_NO_INIT, + STATUS_BAD_VALUE, + STATUS_DEAD_OBJECT, + STATUS_INVALID_OPERATION, + STATUS_TIMED_OUT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RadioStatusType{} + // keep in sync with radio_class_t in /system/core/incluse/system/radio.h /** Radio module class supporting FM (including HD radio) and AM */ @@ -330,6 +348,7 @@ public class RadioManager { * program list. * @return the number of audio sources available. */ + @RadioStatusType public int getNumAudioSources() { return mNumAudioSources; } @@ -1724,6 +1743,7 @@ public class RadioManager { * */ @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioStatusType public int listModules(List modules) { if (modules == null) { Log.e(TAG, "the output list must not be empty"); diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java index 969db96561d78..9b2bcdea5f302 100644 --- a/core/java/android/hardware/radio/RadioTuner.java +++ b/core/java/android/hardware/radio/RadioTuner.java @@ -16,14 +16,20 @@ package android.hardware.radio; +import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.graphics.Bitmap; import android.os.Handler; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; /** * RadioTuner interface provides methods to control a radio tuner on the device: selecting and @@ -43,16 +49,21 @@ public abstract class RadioTuner { public static final int DIRECTION_DOWN = 1; /** - * Close the tuner interface. The {@link Callback} callback will not be called - * anymore and associated resources will be released. - * Must be called when the tuner is not needed to make hardware resources available to others. + * Close the tuner interface. + * + *

The {@link Callback} callback will not be called anymore and associated resources will be + * released. Must be called when the tuner is not needed to make hardware resources available + * to others. * */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void close(); /** * Set the active band configuration for this module. - * Must be a valid configuration obtained via buildConfig() from a valid BandDescriptor listed - * in the ModuleProperties of the module with the specified ID. + * + *

Must be a valid configuration obtained via buildConfig() from a valid BandDescriptor + * listed in the ModuleProperties of the module with the specified ID. + * * @param config The desired band configuration (FmBandConfig or AmBandConfig). * @return *

    @@ -67,10 +78,13 @@ public abstract class RadioTuner { * @deprecated Only applicable for HAL 1.x. */ @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType public abstract int setConfiguration(RadioManager.BandConfig config); /** * Get current configuration. + * * @param config a BandConfig array of lengh 1 where the configuration is returned. * @return *
      @@ -86,11 +100,15 @@ public abstract class RadioTuner { * @deprecated Only applicable for HAL 1.x. */ @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType public abstract int getConfiguration(RadioManager.BandConfig[] config); /** - * Set mute state. When muted, the radio tuner audio source is not available for playback on + * Set mute state. + * + *

      When muted, the radio tuner audio source is not available for playback on * any audio device. when unmuted, the radio tuner audio source is output as a media source * and renderd over the audio device selected for media use case. * The radio tuner audio source is muted by default when the tuner is first attached. @@ -107,6 +125,8 @@ public abstract class RadioTuner { * service fails, *

    */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType public abstract int setMute(boolean mute); /** @@ -115,13 +135,19 @@ public abstract class RadioTuner { * @return {@code true} if the radio tuner audio source is muted or a problem occured * retrieving the mute state, {@code false} otherwise. */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean getMute(); /** * Step up or down by one channel spacing. - * The operation is asynchronous and {@link Callback} - * onProgramInfoChanged() will be called when step completes or - * onError() when cancelled or timeout. + * + *

    The operation is asynchronous and {@link Callback#onProgramInfoChanged} + * will be called when step completes or {@link Callback#onTuneFailed} + * when timeout or canceled. + * + *

    When this operation is called by users other than current user or system user, it is + * ignored silently. + * * @param direction {@link #DIRECTION_UP} or {@link #DIRECTION_DOWN}. * @param skipSubChannel indicates to skip sub channels when the configuration currently * selected supports sub channel (e.g HD Radio). N/A otherwise. @@ -136,13 +162,50 @@ public abstract class RadioTuner { * service fails, *

*/ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType public abstract int step(int direction, boolean skipSubChannel); /** * Scan up or down to next valid station. - * The operation is asynchronous and {@link Callback} - * onProgramInfoChanged() will be called when scan completes or - * onError() when cancelled or timeout. + * + *

The operation is asynchronous and {@link Callback#onProgramInfoChanged} + * will be called when scan completes or {@link Callback#onTuneFailed} + * when timeout or canceled. + * + *

When this operation is called by users other than current user or system user, it is + * ignored silently. + * + * @param direction {@link #DIRECTION_UP} or {@link #DIRECTION_DOWN}. + * @param skipSubChannel indicates to skip sub channels when the configuration currently + * selected supports sub channel (e.g HD Radio). N/A otherwise. + * @return + *

    + *
  • {@link RadioManager#STATUS_OK} in case of success,
  • + *
  • {@link RadioManager#STATUS_ERROR} in case of unspecified error,
  • + *
  • {@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached,
  • + *
  • {@link RadioManager#STATUS_BAD_VALUE} if parameters are invalid,
  • + *
  • {@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence,
  • + *
  • {@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails,
  • + *
+ * @deprecated Use {@link #seek(int, boolean)} instead. + */ + @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType + public abstract int scan(int direction, boolean skipSubChannel); + + /** + * Seek up or down to next valid station. + * + *

The operation is asynchronous and {@link Callback#onProgramInfoChanged} + * will be called when seek completes or {@link Callback#onTuneFailed} + * when timeout or canceled. + * + *

When this operation is called by users other than current user or system user, it is + * ignore silently. + * * @param direction {@link #DIRECTION_UP} or {@link #DIRECTION_DOWN}. * @param skipSubChannel indicates to skip sub channels when the configuration currently * selected supports sub channel (e.g HD Radio). N/A otherwise. @@ -157,13 +220,22 @@ public abstract class RadioTuner { * service fails, * */ - public abstract int scan(int direction, boolean skipSubChannel); + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType + public int seek(int direction, boolean skipSubChannel) { + throw new UnsupportedOperationException("Seeking is not supported"); + } /** * Tune to a specific frequency. - * The operation is asynchronous and {@link Callback} - * onProgramInfoChanged() will be called when tune completes or - * onError() when cancelled or timeout. + * + *

The operation is asynchronous and {@link Callback#onProgramInfoChanged} + * will be called when tune completes or {@link Callback#onTuneFailed} + * when timeout or canceled. + * + *

When this operation is called by users other than current user or system user, it is + * ignored silently. + * * @param channel the specific channel or frequency to tune to. * @param subChannel the specific sub-channel to tune to. N/A if the selected configuration * does not support cub channels. @@ -177,25 +249,37 @@ public abstract class RadioTuner { *

  • {@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native * service fails,
  • * - * @deprecated Use {@link tune(ProgramSelector)} instead. + * @deprecated Use {@link #tune(ProgramSelector)} instead. */ @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType public abstract int tune(int channel, int subChannel); /** * Tune to a program. * - * The operation is asynchronous and {@link Callback} onProgramInfoChanged() will be called - * when tune completes or onError() when cancelled or on timeout. + *

    The operation is asynchronous and {@link Callback#onProgramInfoChanged} + * will be called when tune completes or {@link Callback#onTuneFailed} + * when timeout or canceled. + * + *

    When this operation is called by users other than current user or system user, it is + * ignored silently. * * @throws IllegalArgumentException if the provided selector is invalid */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void tune(@NonNull ProgramSelector selector); /** * Cancel a pending scan or tune operation. - * If an operation is pending, {@link Callback} onError() will be called with + * + *

    If an operation is pending, {@link Callback#onTuneFailed} will be called with * {@link #ERROR_CANCELLED}. + * + *

    When this operation is called by users other than current user or system + * user, it is ignored silently. + * * @return *

      *
    • {@link RadioManager#STATUS_OK} in case of success,
    • @@ -207,21 +291,27 @@ public abstract class RadioTuner { * service fails, *
    */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType public abstract int cancel(); /** * Cancels traffic or emergency announcement. * - * If there was no announcement to cancel, no action is taken. + *

    If there was no announcement to cancel, no action is taken. * - * There is a race condition between calling cancelAnnouncement and the actual announcement + *

    There is a race condition between calling cancelAnnouncement and the actual announcement * being finished, so onTrafficAnnouncement / onEmergencyAnnouncement callback should be * tracked with proper locking. + * @deprecated Only applicable for HAL 1.x. */ + @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void cancelAnnouncement(); /** * Get current station information. + * * @param info a ProgramInfo array of lengh 1 where the information is returned. * @return *

      @@ -233,23 +323,27 @@ public abstract class RadioTuner { *
    • {@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native * service fails,
    • *
    - * @deprecated Use {@link onProgramInfoChanged} callback instead. + * @deprecated Use {@link Callback#onProgramInfoChanged(RadioManager.ProgramInfo)} callback + * instead. */ @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) + @RadioManager.RadioStatusType public abstract int getProgramInformation(RadioManager.ProgramInfo[] info); /** * Retrieves a {@link Bitmap} for the given image ID or null, * if the image was missing from the tuner. * - * This involves doing a call to the tuner, so the bitmap should be cached + *

    This involves doing a call to the tuner, so the bitmap should be cached * on the application side. * - * If the method returns null for non-zero ID, it means the image was + *

    If the method returns null for non-zero ID, it means the image was * updated on the tuner side. There is a race conditon between fetching * image for an old ID and tuner updating the image (and cleaning up the * old image). In such case, a new ProgramInfo with updated image id will - * be sent with a {@link onProgramInfoChanged} callback. + * be sent with a {@link Callback#onProgramInfoChanged(RadioManager.ProgramInfo)} + * callback. * * @param id The image identifier, retrieved with * {@link RadioMetadata#getBitmapId(String)}. @@ -258,14 +352,16 @@ public abstract class RadioTuner { * @hide This API is not thoroughly elaborated yet */ @SuppressWarnings("HiddenAbstractMethod") + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract @Nullable Bitmap getMetadataImage(int id); /** * Initiates a background scan to update internally cached program list. * - * It may not be necessary to initiate the scan explicitly - the scan MAY be performed on boot. + *

    It may not be necessary to initiate the scan explicitly - the scan MAY be performed on + * boot. * - * The operation is asynchronous and {@link Callback} backgroundScanComplete or onError will + *

    The operation is asynchronous and {@link Callback} backgroundScanComplete or onError will * be called if the return value of this call was {@code true}. As result of this call * programListChanged may be triggered (if the scanned list differs). * @@ -273,13 +369,16 @@ public abstract class RadioTuner { * is unavailable; ie. temporarily due to ongoing foreground playback in single-tuner device * or permanently if the feature is not supported * (see ModuleProperties#isBackgroundScanningSupported()). + * @deprecated Only applicable for HAL 1.x. */ + @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean startBackgroundScan(); /** * Get the list of discovered radio stations. * - * To get the full list, set filter to null or empty map. + *

    To get the full list, set filter to null or empty map. * Keys must be prefixed with unique vendor Java-style namespace, * eg. 'com.somecompany.parameter1'. * @@ -288,24 +387,27 @@ public abstract class RadioTuner { * @throws IllegalStateException if the scan is in progress or has not been started, * startBackgroundScan() call may fix it. * @throws IllegalArgumentException if the vendorFilter argument is not valid. - * @deprecated Use {@link getDynamicProgramList} instead. + * @deprecated Use {@link #getDynamicProgramList(ProgramList.Filter)} instead. */ @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract @NonNull List getProgramList(@Nullable Map vendorFilter); /** * Get the dynamic list of discovered radio stations. * - * The list object is updated asynchronously; to get the updates register - * with {@link ProgramList#addListCallback}. + *

    The list object is updated asynchronously; to get the updates register + * with {@link ProgramList#registerListCallback(ProgramList.ListCallback)} + * or {@link ProgramList#registerListCallback(Executor, ProgramList.ListCallback)}. * - * When the returned object is no longer used, it must be closed. + *

    When the returned object is no longer used, it must be closed. * * @param filter filter for the list, or null to get the full list. * @return the dynamic program list object, close it after use * or {@code null} if program list is not supported by the tuner */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public @Nullable ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) { return null; } @@ -316,26 +418,28 @@ public abstract class RadioTuner { * @throws IllegalStateException if the switch is not supported at current * configuration. * @return {@code true} if analog is forced, {@code false} otherwise. - * @deprecated Use {@link isConfigFlagSet(int)} instead. + * @deprecated Use {@link #isConfigFlagSet(int)} instead. */ @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean isAnalogForced(); /** * Forces the analog playback for the supporting radio technology. * - * User may disable digital playback for FM HD Radio or hybrid FM/DAB with + *

    User may disable digital playback for FM HD Radio or hybrid FM/DAB with * this option. This is purely user choice, ie. does not reflect digital- * analog handover managed from the HAL implementation side. * - * Some radio technologies may not support this, ie. DAB. + *

    Some radio technologies may not support this, ie. DAB. * * @param isForced {@code true} to force analog, {@code false} for a default behaviour. * @throws IllegalStateException if the switch is not supported at current * configuration. - * @deprecated Use {@link setConfigFlag(int, boolean)} instead. + * @deprecated Use {@link #setConfigFlag(int, boolean)} instead. */ @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract void setAnalogForced(boolean isForced); /** @@ -344,6 +448,7 @@ public abstract class RadioTuner { * @param flag Flag to check. * @return True, if the flag is supported. */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public boolean isConfigFlagSupported(@RadioManager.ConfigFlag int flag) { return false; } @@ -351,29 +456,34 @@ public abstract class RadioTuner { /** * Fetches the current setting of a given config flag. * - * The success/failure result is consistent with isConfigFlagSupported. + *

    The success/failure result is consistent with isConfigFlagSupported. * * @param flag Flag to fetch. * @return The current value of the flag. * @throws IllegalStateException if the flag is not applicable right now. * @throws UnsupportedOperationException if the flag is not supported at all. */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public boolean isConfigFlagSet(@RadioManager.ConfigFlag int flag) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("isConfigFlagSet is not supported"); } /** * Sets the config flag. * - * The success/failure result is consistent with isConfigFlagSupported. + *

    The success/failure result is consistent with isConfigFlagSupported. + * + *

    When this operation is called by users other than current user or system user, + * it is ignored silently. * * @param flag Flag to set. * @param value The new value of a given flag. * @throws IllegalStateException if the flag is not applicable right now. * @throws UnsupportedOperationException if the flag is not supported at all. */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public void setConfigFlag(@RadioManager.ConfigFlag int flag, boolean value) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("Setting config flag is not supported"); } /** @@ -381,30 +491,37 @@ public abstract class RadioTuner { * The framework does not interpret the parameters, they are passed * in an opaque manner between a vendor application and HAL. * - * Framework does not make any assumptions on the keys or values, other than + *

    Framework does not make any assumptions on the keys or values, other than * ones stated in VendorKeyValue documentation (a requirement of key * prefixes). - * See VendorKeyValue at hardware/interfaces/broadcastradio/2.0/types.hal. + * See VendorKeyValue at hardware/interfaces/broadcastradio/2.0/types.hal for + * HIDL 2.0 HAL or + * hardware/interfaces/broadcastradio/aidl/android/hardware/broadcastradio/VendorKeyValue.aidl + * for AIDL HAL. * - * For each pair in the result map, the key will be one of the keys + *

    For each pair in the result map, the key will be one of the keys * contained in the input (possibly with wildcards expanded), and the value * will be a vendor-specific result status (such as "OK" or an error code). * The implementation may choose to return an empty map, or only return * a status for a subset of the provided inputs, at its discretion. * - * Application and HAL must not use keys with unknown prefix. In particular, + *

    Application and HAL must not use keys with unknown prefix. In particular, * it must not place a key-value pair in results vector for unknown key from * parameters vector - instead, an unknown key should simply be ignored. * In other words, results vector may contain a subset of parameter keys * (however, the framework doesn't enforce a strict subset - the only * formal requirement is vendor domain prefix for keys). * + *

    When this operation is called by users other than current user or system user, + * it is ignored silently. + * * @param parameters Vendor-specific key-value pairs. * @return Operation completion status for parameters being set. */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public @NonNull Map setParameters(@NonNull Map parameters) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("Setting parameters is not supported"); } /** @@ -412,23 +529,24 @@ public abstract class RadioTuner { * The framework does not interpret the parameters, they are passed * in an opaque manner between a vendor application and HAL. * - * Framework does not cache set/get requests, so it's possible for + *

    Framework does not cache set/get requests, so it's possible for * getParameter to return a different value than previous setParameter call. * - * The syntax and semantics of keys are up to the vendor (as long as prefix + *

    The syntax and semantics of keys are up to the vendor (as long as prefix * rules are obeyed). For instance, vendors may include some form of * wildcard support. In such case, result vector may be of different size * than requested keys vector. However, wildcards are not recognized by * framework and they are passed as-is to the HAL implementation. * - * Unknown keys must be ignored and not placed into results vector. + *

    Unknown keys must be ignored and not placed into results vector. * * @param keys Parameter keys to fetch. * @return Vendor-specific key-value pairs. */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public @NonNull Map getParameters(@NonNull List keys) { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("Getting parameters is not supported"); } /** @@ -436,14 +554,16 @@ public abstract class RadioTuner { * Only valid if a configuration has been applied. * @return {@code true} if the antenna is connected, {@code false} otherwise. * - * @deprecated Use {@link onAntennaState} callback instead + * @deprecated Use {@link Callback#onAntennaState(boolean)} callback instead */ @Deprecated + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean isAntennaConnected(); /** * Indicates if this client actually controls the tuner. - * Control is always granted after + * + *

    Control is always granted after * {@link RadioManager#openTuner(int, * RadioManager.BandConfig, boolean, Callback, Handler)} * returns a non null tuner interface. @@ -451,48 +571,102 @@ public abstract class RadioTuner { * When this happens, {@link Callback#onControlChanged(boolean)} is received. * The client can either wait for control to be returned (which is indicated by the same * callback) or close and reopen the tuner interface. + * * @return {@code true} if this interface controls the tuner, * {@code false} otherwise or if a problem occured retrieving the state. */ + @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract boolean hasControl(); /** Indicates a failure of radio IC or driver. - * The application must close and re open the tuner - * @deprecated See {@link onError} callback. + * + *

    The application must close and re open the tuner + * + * @deprecated See {@link RadioTuner.Callback#onError(int)} callback. */ @Deprecated public static final int ERROR_HARDWARE_FAILURE = 0; /** Indicates a failure of the radio service. - * The application must close and re open the tuner - * @deprecated See {@link onError} callback. + * + *

    The application must close and re open the tuner + * @deprecated See {@link RadioTuner.Callback#onError(int)} callback. */ @Deprecated public static final int ERROR_SERVER_DIED = 1; /** A pending seek or tune operation was cancelled - * @deprecated See {@link onError} callback. + * @deprecated See {@link RadioTuner.Callback#onError(int)} callback. */ @Deprecated public static final int ERROR_CANCELLED = 2; /** A pending seek or tune operation timed out - * @deprecated See {@link onError} callback. + * @deprecated See {@link RadioTuner.Callback#onError(int)} callback. */ @Deprecated public static final int ERROR_SCAN_TIMEOUT = 3; /** The requested configuration could not be applied - * @deprecated See {@link onError} callback. + * @deprecated See {@link RadioTuner.Callback#onError(int)} callback. */ @Deprecated public static final int ERROR_CONFIG = 4; /** Background scan was interrupted due to hardware becoming temporarily unavailable. - * @deprecated See {@link onError} callback. + * @deprecated See {@link RadioTuner.Callback#onError(int)} callback. */ @Deprecated public static final int ERROR_BACKGROUND_SCAN_UNAVAILABLE = 5; /** Background scan failed due to other error, ie. HW failure. - * @deprecated See {@link onError} callback. + * @deprecated See {@link RadioTuner.Callback#onError(int)} callback. */ @Deprecated public static final int ERROR_BACKGROUND_SCAN_FAILED = 6; + /** Result when a tune, seek, or step operation runs without error. + */ + public static final int TUNER_RESULT_OK = 0; + /** Result when internal error occurs in HAL. + * See {@link RadioTuner.Callback#onTuneFailed(int, ProgramSelector)} callback. + */ + public static final int TUNER_RESULT_INTERNAL_ERROR = 1; + /** Result used when the input argument for the method is invalid. + * See {@link RadioTuner.Callback#onTuneFailed(int, ProgramSelector)} callback. + */ + public static final int TUNER_RESULT_INVALID_ARGUMENTS = 2; + /** Result when HAL is of invalid state. + * See {@link RadioTuner.Callback#onTuneFailed(int, ProgramSelector)} callback. + */ + public static final int TUNER_RESULT_INVALID_STATE = 3; + /** Result when the operation is not supported. + * See {@link RadioTuner.Callback#onTuneFailed(int, ProgramSelector)} callback. + */ + public static final int TUNER_RESULT_NOT_SUPPORTED = 4; + /** Result when a tune, seek, or step operation is timeout + * See {@link RadioTuner.Callback#onTuneFailed(int, ProgramSelector)} callback. + */ + public static final int TUNER_RESULT_TIMEOUT = 5; + /** Result when a tune, seek, or step operation is canceled before processed. + * See {@link RadioTuner.Callback#onTuneFailed(int, ProgramSelector)} callback. + */ + public static final int TUNER_RESULT_CANCELED = 6; + /** Result when a tune, seek, or step operation fails due to unknown error. + * See {@link RadioTuner.Callback#onTuneFailed(int, ProgramSelector)} callback. + */ + public static final int TUNER_RESULT_UNKNOWN_ERROR = 7; + + /** + * Tuning operation result types + * + * @hide + */ + @IntDef(prefix = { "TUNER_RESULT_" }, value = { + TUNER_RESULT_OK, + TUNER_RESULT_INTERNAL_ERROR, + TUNER_RESULT_INVALID_ARGUMENTS, + TUNER_RESULT_INVALID_STATE, + TUNER_RESULT_NOT_SUPPORTED, + TUNER_RESULT_TIMEOUT, + TUNER_RESULT_CANCELED, + TUNER_RESULT_UNKNOWN_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TunerResultType{} /** * Callback provided by the client application when opening a {@link RadioTuner} @@ -506,8 +680,9 @@ public abstract class RadioTuner { * {@link #ERROR_CANCELLED}, {@link #ERROR_SCAN_TIMEOUT}, * {@link #ERROR_CONFIG} * - * @deprecated Use {@link onTuneFailed} for tune, scan and step; - * other use cases (configuration, background scan) are already deprecated. + * @deprecated Use {@link RadioTuner.Callback#onTuneFailed(int, ProgramSelector)} for + * tune, scan and step; other use cases (configuration, background scan) + * are already deprecated. */ public void onError(int status) {} @@ -518,7 +693,7 @@ public abstract class RadioTuner { * @param selector ProgramSelector argument of tune that failed; * null for scan and step. */ - public void onTuneFailed(int result, @Nullable ProgramSelector selector) {} + public void onTuneFailed(@TunerResultType int result, @Nullable ProgramSelector selector) {} /** * onConfigurationChanged() is called upon successful completion of @@ -533,7 +708,7 @@ public abstract class RadioTuner { /** * Called when program info (including metadata) for the current program has changed. * - * It happens either upon successful completion of {@link RadioTuner#step(int, boolean)}, + *

    It happens either upon successful completion of {@link RadioTuner#step(int, boolean)}, * {@link RadioTuner#scan(int, boolean)}, {@link RadioTuner#tune(int, int)}; when * a switching to alternate frequency occurs; or when metadata is updated. */ @@ -589,16 +764,29 @@ public abstract class RadioTuner { /** * Called when available program list changed. * - * Use {@link RadioTuner#getProgramList(String)} to get an actual list. + * Use {@link RadioTuner#getProgramList(Map)} to get an actual list. */ public void onProgramListChanged() {} + /** + * Called when config flags are updated asynchronously due to internal events + * in broadcast radio HAL. + * + * {@link RadioTuner#setConfigFlag(int, boolean)} must not trigger this + * callback. + * + * @param flag Config flag updated + * @param value Value of the updated config flag + */ + public void onConfigFlagUpdated(@RadioManager.ConfigFlag int flag, boolean value) {} + /** * Generic callback for passing updates to vendor-specific parameter values. - * The framework does not interpret the parameters, they are passed + * + *

    The framework does not interpret the parameters, they are passed * in an opaque manner between a vendor application and HAL. * - * It's up to the HAL implementation if and how to implement this callback, + *

    It's up to the HAL implementation if and how to implement this callback, * as long as it obeys the prefix rule. In particular, only selected keys * may be notified this way. However, setParameters must not trigger * this callback, while an internal event can change parameters diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java index 4a18333aee9f7..bdbca91a715a9 100644 --- a/core/java/android/hardware/radio/TunerAdapter.java +++ b/core/java/android/hardware/radio/TunerAdapter.java @@ -154,7 +154,7 @@ final class TunerAdapter extends RadioTuner { @Override public int scan(int direction, boolean skipSubChannel) { try { - mTuner.scan(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN, + mTuner.seek(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN, skipSubChannel); } catch (IllegalStateException e) { Log.e(TAG, "Can't scan", e); @@ -166,6 +166,21 @@ final class TunerAdapter extends RadioTuner { return RadioManager.STATUS_OK; } + @Override + public int seek(int direction, boolean skipSubChannel) { + try { + mTuner.seek(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN, + skipSubChannel); + } catch (IllegalStateException e) { + Log.e(TAG, "Can't seek", e); + return RadioManager.STATUS_INVALID_OPERATION; + } catch (RemoteException e) { + Log.e(TAG, "Service died", e); + return RadioManager.STATUS_DEAD_OBJECT; + } + return RadioManager.STATUS_OK; + } + @Override public int tune(int channel, int subChannel) { try { diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java index b9782a87735db..22f59021bca17 100644 --- a/core/java/android/hardware/radio/TunerCallbackAdapter.java +++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java @@ -245,6 +245,11 @@ final class TunerCallbackAdapter extends ITunerCallback.Stub { }); } + @Override + public void onConfigFlagUpdated(@RadioManager.ConfigFlag int flag, boolean value) { + mHandler.post(() -> mCallback.onConfigFlagUpdated(flag, value)); + } + @Override public void onParametersUpdated(Map parameters) { mHandler.post(() -> mCallback.onParametersUpdated(parameters)); diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java index 3f35e998e0253..cabeb13b21240 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java @@ -17,6 +17,7 @@ package android.hardware.radio.tests.functional; import static org.junit.Assert.*; import static org.junit.Assume.*; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -335,8 +336,10 @@ public class RadioTunerTest { assertEquals(RadioManager.STATUS_OK, scanRet); assertEquals(RadioManager.STATUS_OK, cancelRet); - verify(mCallback, after(kCancelTimeoutMs).atMost(1)).onError(RadioTuner.ERROR_CANCELLED); + verify(mCallback, after(kCancelTimeoutMs).atMost(1)) + .onTuneFailed(eq(RadioTuner.TUNER_RESULT_CANCELED), any()); verify(mCallback, atMost(1)).onProgramInfoChanged(any()); + Mockito.reset(mCallback); } @Test diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java index 2fa3f87668ca0..65e55a2c753bc 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/DefaultRadioTunerTest.java @@ -129,6 +129,18 @@ public final class DefaultRadioTunerTest { } }; + @Test + public void seek_forRadioTuner_throwsException() { + UnsupportedOperationException thrown = assertThrows( + UnsupportedOperationException.class, () -> { + DEFAULT_RADIO_TUNER.seek(RadioTuner.DIRECTION_DOWN, + /* skipSubChannel= */ false); + }); + + assertWithMessage("Exception for seeking on default radio tuner") + .that(thrown).hasMessageThat().contains("Seeking is not supported"); + } + @Test public void getDynamicProgramList_forRadioTuner_returnsNull() { assertWithMessage("Dynamic program list obtained from default radio tuner") @@ -143,29 +155,45 @@ public final class DefaultRadioTunerTest { @Test public void isConfigFlagSet_forRadioTuner_throwsException() { - assertThrows(UnsupportedOperationException.class, () -> { - DEFAULT_RADIO_TUNER.isConfigFlagSet(/* flag= */ 1); - }); + UnsupportedOperationException thrown = assertThrows( + UnsupportedOperationException.class, () -> { + DEFAULT_RADIO_TUNER.isConfigFlagSet(/* flag= */ 1); + }); + + assertWithMessage("Exception for isConfigFlagSet on default radio tuner") + .that(thrown).hasMessageThat().contains("isConfigFlagSet is not supported"); } @Test public void setConfigFlag_forRadioTuner_throwsException() { - assertThrows(UnsupportedOperationException.class, () -> { + UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, + () -> { DEFAULT_RADIO_TUNER.setConfigFlag(/* flag= */ 1, /* value= */ false); }); + + assertWithMessage("Exception for setting config flag on default radio tuner") + .that(thrown).hasMessageThat().contains("Setting config flag is not supported"); } @Test public void setParameters_forRadioTuner_throwsException() { - assertThrows(UnsupportedOperationException.class, () -> { + UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, + () -> { DEFAULT_RADIO_TUNER.setParameters(Map.of("testKey", "testValue")); }); + + assertWithMessage("Exception for setting parameters from default radio tuner") + .that(thrown).hasMessageThat().contains("Setting parameters is not supported"); } @Test public void getParameters_forRadioTuner_throwsException() { - assertThrows(UnsupportedOperationException.class, () -> { + UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, + () -> { DEFAULT_RADIO_TUNER.getParameters(List.of("testKey")); }); + + assertWithMessage("Exception for getting parameters from default radio tuner") + .that(thrown).hasMessageThat().contains("Getting parameters is not supported"); } } diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java index bdba6a1c5402a..6d77f99361964 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java @@ -55,7 +55,6 @@ public final class TunerAdapterTest { private static final int AM_LOWER_LIMIT_KHZ = 150; private static final RadioManager.BandConfig TEST_BAND_CONFIG = createBandConfig(); - private static final ProgramSelector.Identifier FM_IDENTIFIER = new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, /* value= */ 94300); @@ -169,16 +168,31 @@ public final class TunerAdapterTest { verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO); } + @Test + public void scan_forTunerAdapter_succeeds() throws Exception { + doAnswer(invocation -> { + mTunerCallback.onCurrentProgramInfoChanged(FM_PROGRAM_INFO); + return RadioManager.STATUS_OK; + }).when(mTunerMock).seek(anyBoolean(), anyBoolean()); + + int scanStatus = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, /* skipSubChannel= */ false); + + verify(mTunerMock).seek(/* directionDown= */ true, /* skipSubChannel= */ false); + assertWithMessage("Status for scaning") + .that(scanStatus).isEqualTo(RadioManager.STATUS_OK); + verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO); + } + @Test public void seek_forTunerAdapter_succeeds() throws Exception { doAnswer(invocation -> { mTunerCallback.onCurrentProgramInfoChanged(FM_PROGRAM_INFO); return RadioManager.STATUS_OK; - }).when(mTunerMock).scan(anyBoolean(), anyBoolean()); + }).when(mTunerMock).seek(anyBoolean(), anyBoolean()); int scanStatus = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, /* skipSubChannel= */ false); - verify(mTunerMock).scan(/* directionDown= */ true, /* skipSubChannel= */ false); + verify(mTunerMock).seek(/* directionDown= */ true, /* skipSubChannel= */ false); assertWithMessage("Status for seeking") .that(scanStatus).isEqualTo(RadioManager.STATUS_OK); verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO); @@ -187,13 +201,14 @@ public final class TunerAdapterTest { @Test public void seek_forTunerAdapter_invokesOnErrorWhenTimeout() throws Exception { doAnswer(invocation -> { - mTunerCallback.onError(RadioTuner.ERROR_SCAN_TIMEOUT); + mTunerCallback.onTuneFailed(RadioManager.STATUS_TIMED_OUT, FM_SELECTOR); return RadioManager.STATUS_OK; - }).when(mTunerMock).scan(anyBoolean(), anyBoolean()); + }).when(mTunerMock).seek(anyBoolean(), anyBoolean()); mRadioTuner.scan(RadioTuner.DIRECTION_UP, /* skipSubChannel*/ true); - verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onError(RadioTuner.ERROR_SCAN_TIMEOUT); + verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onTuneFailed( + RadioManager.STATUS_TIMED_OUT, FM_SELECTOR); } @Test @@ -414,6 +429,17 @@ public final class TunerAdapterTest { verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramListChanged(); } + @Test + public void onConfigFlagUpdated_forTunerCallbackAdapter() throws Exception { + int configFlag = RadioManager.CONFIG_RDS_AF; + boolean configFlagValue = true; + + mTunerCallback.onConfigFlagUpdated(configFlag, configFlagValue); + + verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)) + .onConfigFlagUpdated(configFlag, configFlagValue); + } + @Test public void onParametersUpdated_forTunerCallbackAdapter() throws Exception { Map parametersExpected = Map.of("ParameterKeyMock", "ParameterValueMock"); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index 993ca7728374b..b76171aef4663 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -377,28 +377,28 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test - public void scan_withDirectionUp() throws Exception { + public void seek_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[2]; ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); - RadioManager.ProgramInfo scanUpInfo = AidlTestUtils.makeProgramInfo( + RadioManager.ProgramInfo seekUpInfo = AidlTestUtils.makeProgramInfo( AidlTestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ false)), SIGNAL_QUALITY); openAidlClients(/* numClients= */ 1); mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo( ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY); - mTunerSessions[0].scan(/* directionDown= */ false, /* skipSubChannel= */ false); + mTunerSessions[0].seek(/* directionDown= */ false, /* skipSubChannel= */ false); verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) - .onCurrentProgramInfoChanged(scanUpInfo); + .onCurrentProgramInfoChanged(seekUpInfo); } @Test - public void scan_callsOnTuneFailedWhenTimeout() throws Exception { + public void seek_callsOnTuneFailedWhenTimeout() throws Exception { int numSessions = 2; openAidlClients(numSessions); - mTunerSessions[0].scan(/* directionDown= */ false, /* skipSubChannel= */ false); + mTunerSessions[0].seek(/* directionDown= */ false, /* skipSubChannel= */ false); for (int index = 0; index < numSessions; index++) { verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) @@ -407,19 +407,19 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test - public void scan_withDirectionDown() throws Exception { + public void seek_withDirectionDown() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[2]; ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); - RadioManager.ProgramInfo scanUpInfo = AidlTestUtils.makeProgramInfo( + RadioManager.ProgramInfo seekUpInfo = AidlTestUtils.makeProgramInfo( AidlTestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ true)), SIGNAL_QUALITY); openAidlClients(/* numClients= */ 1); mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo( ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY); - mTunerSessions[0].scan(/* directionDown= */ true, /* skipSubChannel= */ false); + mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) - .onCurrentProgramInfoChanged(scanUpInfo); + .onCurrentProgramInfoChanged(seekUpInfo); } @Test @@ -585,7 +585,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test - public void onConfigFlagUpdated_forTunerCallback() throws Exception { + public void onAntennaStateChange_forTunerCallback() throws Exception { int numSessions = 3; openAidlClients(numSessions); @@ -597,6 +597,21 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } } + @Test + public void onConfigFlagUpdated_forTunerCallback() throws Exception { + int numSessions = 3; + openAidlClients(numSessions); + int flag = UNSUPPORTED_CONFIG_FLAG + 1; + boolean configFlagValue = true; + + mHalTunerCallback.onConfigFlagUpdated(flag, configFlagValue); + + for (int index = 0; index < numSessions; index++) { + verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) + .onConfigFlagUpdated(flag, configFlagValue); + } + } + @Test public void onParametersUpdated_forTunerCallback() throws Exception { int numSessions = 3; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java index ff988a21473ad..6b75d8d0b5dc6 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java @@ -384,28 +384,28 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } @Test - public void scan_withDirectionUp() throws Exception { + public void seek_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[2]; ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq); - RadioManager.ProgramInfo scanUpInfo = TestUtils.makeProgramInfo( + RadioManager.ProgramInfo seekUpInfo = TestUtils.makeProgramInfo( TestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ false)), SIGNAL_QUALITY); openAidlClients(/* numClients= */ 1); mHalCurrentInfo = TestUtils.makeHalProgramInfo( Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY); - mTunerSessions[0].scan(/* directionDown= */ false, /* skipSubChannel= */ false); + mTunerSessions[0].seek(/* directionDown= */ false, /* skipSubChannel= */ false); verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) - .onCurrentProgramInfoChanged(scanUpInfo); + .onCurrentProgramInfoChanged(seekUpInfo); } @Test - public void scan_callsOnTuneFailedWhenTimeout() throws Exception { + public void seek_callsOnTuneFailedWhenTimeout() throws Exception { int numSessions = 2; openAidlClients(numSessions); - mTunerSessions[0].scan(/* directionDown= */ false, /* skipSubChannel= */ false); + mTunerSessions[0].seek(/* directionDown= */ false, /* skipSubChannel= */ false); for (int index = 0; index < numSessions; index++) { verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) @@ -414,19 +414,19 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } @Test - public void scan_withDirectionDown() throws Exception { + public void seek_withDirectionDown() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[2]; ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq); - RadioManager.ProgramInfo scanUpInfo = TestUtils.makeProgramInfo( + RadioManager.ProgramInfo seekUpInfo = TestUtils.makeProgramInfo( TestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ true)), SIGNAL_QUALITY); openAidlClients(/* numClients= */ 1); mHalCurrentInfo = TestUtils.makeHalProgramInfo( Convert.programSelectorToHal(initialSel), SIGNAL_QUALITY); - mTunerSessions[0].scan(/* directionDown= */ true, /* skipSubChannel= */ false); + mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) - .onCurrentProgramInfoChanged(scanUpInfo); + .onCurrentProgramInfoChanged(seekUpInfo); } @Test diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java index d90f9c47f10ac..09ee99659afca 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java @@ -143,6 +143,7 @@ final class ConversionUtils { case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE: case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID: case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY: + case ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT: return ProgramSelector.PROGRAM_TYPE_DAB; case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID: case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY: diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java index e956a9c2038cc..5a3bd28cb7047 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java @@ -147,8 +147,9 @@ final class RadioModule { @Override public void onConfigFlagUpdated(int flag, boolean value) { fireLater(() -> { - // TODO(b/243853343): implement config flag update method in - // android.hardware.radio.ITunerCallback + synchronized (mLock) { + fanoutAidlCallbackLocked(cb -> cb.onConfigFlagUpdated(flag, value)); + } }); } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java index 1ce4044d4c51c..66e1be183a0f2 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java @@ -178,8 +178,8 @@ final class TunerSession extends ITuner.Stub { } @Override - public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException { - mLogger.logRadioEvent("Scan with direction %s, skipSubChannel? %s", + public void seek(boolean directionDown, boolean skipSubChannel) throws RemoteException { + mLogger.logRadioEvent("Seek with direction %s, skipSubChannel? %s", directionDown ? "down" : "up", skipSubChannel ? "yes" : "no"); if (!RadioServiceUserController.isCurrentOrSystemUser()) { Slogf.w(TAG, "Cannot scan on AIDL HAL client from non-current user"); @@ -232,8 +232,7 @@ final class TunerSession extends ITuner.Stub { @Override public void cancelAnnouncement() { - // TODO(b/244485175): deperacte cancelAnnouncement - Slogf.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in AIDL"); + Slogf.w(TAG, "Announcements control doesn't involve cancelling at the HAL level in AIDL"); } @Override @@ -244,7 +243,7 @@ final class TunerSession extends ITuner.Stub { @Override public boolean startBackgroundScan() { - Slogf.i(TAG, "Explicit background scan trigger is not supported with HAL AIDL"); + Slogf.w(TAG, "Explicit background scan trigger is not supported with HAL AIDL"); if (!RadioServiceUserController.isCurrentOrSystemUser()) { Slogf.w(TAG, "Cannot start background scan on AIDL HAL client from non-current user"); return false; diff --git a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java index ed8a37aab14a3..8e5f6b5b86240 100644 --- a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java +++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java @@ -189,9 +189,9 @@ class Tuner extends ITuner.Stub { } @Override - public void scan(boolean directionDown, boolean skipSubChannel) { + public void seek(boolean directionDown, boolean skipSubChannel) { if (!RadioServiceUserController.isCurrentOrSystemUser()) { - Slogf.w(TAG, "Cannot scan on HAL 1.x client from non-current user"); + Slogf.w(TAG, "Cannot seek on HAL 1.x client from non-current user"); return; } synchronized (mLock) { diff --git a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java index 0cc3833896bec..aa43b7581fe7e 100644 --- a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java +++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java @@ -173,9 +173,14 @@ class TunerCallback implements ITunerCallback { dispatch(() -> mClientCallback.onProgramListUpdated(chunk)); } + @Override + public void onConfigFlagUpdated(int flag, boolean value) { + Slog.w(TAG, "Not applicable for HAL 1.x"); + } + @Override public void onParametersUpdated(Map parameters) { - Slog.e(TAG, "Not applicable for HAL 1.x"); + Slog.w(TAG, "Not applicable for HAL 1.x"); } @Override diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index 3daf1dbd933ae..620364dab3b5a 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -130,6 +130,7 @@ class Convert { case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE: case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID: case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY: + case ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT: return ProgramSelector.PROGRAM_TYPE_DAB; case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID: case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY: diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java index 7afee277fe1c6..204b9649135e4 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java @@ -171,8 +171,8 @@ class TunerSession extends ITuner.Stub { } @Override - public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException { - mEventLogger.logRadioEvent("Scan with direction %s, skipSubChannel? %s", + public void seek(boolean directionDown, boolean skipSubChannel) throws RemoteException { + mEventLogger.logRadioEvent("Seek with direction %s, skipSubChannel? %s", directionDown ? "down" : "up", skipSubChannel ? "yes" : "no"); if (!RadioServiceUserController.isCurrentOrSystemUser()) { Slogf.w(TAG, "Cannot scan on HAL 2.0 client from non-current user"); @@ -214,7 +214,7 @@ class TunerSession extends ITuner.Stub { @Override public void cancelAnnouncement() { - Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in HAL 2.0"); + Slog.w(TAG, "Announcements control doesn't involve cancelling at the HAL level in HAL 2.0"); } @Override @@ -225,7 +225,7 @@ class TunerSession extends ITuner.Stub { @Override public boolean startBackgroundScan() { - Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.0"); + Slog.w(TAG, "Explicit background scan trigger is not supported with HAL 2.0"); if (!RadioServiceUserController.isCurrentOrSystemUser()) { Slogf.w(TAG, "Cannot start background scan on HAL 2.0 client from non-current user"); From c0b7cdc45971590096cc7a16352a431b361ee755 Mon Sep 17 00:00:00 2001 From: Weilin Xu Date: Wed, 7 Dec 2022 00:21:40 +0000 Subject: [PATCH 2/3] Add SDK version checking in AIDL radio HAL client The target SDK version checking was added in AIDL broadcast radio HAL to guarantee that broadcast radio HAL can pass object including new HAL features to the application only when the application has a target SDK version at least the SDK version with the new features. If the application SDK version is lower, the program selectors and program infos will not be sent back to applications if they contain features only existing in higher SDK version, and items only existing in higher version will be removed from the chunk before sending back to applications. HIDL 1.x and 2.0 HAL clients were not updated with SDK version since new broadcast radio HAL features are no longer landed in them. Bug: 257337458 Test: atest android.hardware.radio.tests.unittests Test: atest com.android.server.broadcastradio Change-Id: Ia716353d9c796c5dc5f5218d33fd20a129a5370a --- .../android/hardware/radio/IRadioService.aidl | 2 +- .../android/hardware/radio/RadioManager.java | 4 +- .../tests/unittests/ProgramListTest.java | 10 ++-- .../tests/unittests/RadioManagerTest.java | 15 ++++- .../tests/unittests/TunerAdapterTest.java | 9 ++- .../IRadioServiceAidlImplTest.java | 7 ++- .../IRadioServiceHidlImplTest.java | 6 +- .../aidl/BroadcastRadioServiceImplTest.java | 14 +++-- .../aidl/ConversionUtilsTest.java | 13 ++++ .../broadcastradio/aidl/TunerSessionTest.java | 22 ++++++- .../broadcastradio/IRadioServiceAidlImpl.java | 5 +- .../broadcastradio/IRadioServiceHidlImpl.java | 3 +- .../aidl/BroadcastRadioServiceImpl.java | 5 +- .../broadcastradio/aidl/ConversionUtils.java | 60 +++++++++++++++++++ .../broadcastradio/aidl/RadioModule.java | 38 +++++++++--- .../broadcastradio/aidl/TunerSession.java | 24 ++++++-- .../hal2/BroadcastRadioService.java | 2 +- 17 files changed, 199 insertions(+), 40 deletions(-) diff --git a/core/java/android/hardware/radio/IRadioService.aidl b/core/java/android/hardware/radio/IRadioService.aidl index 9349cf7d0c327..c7131a7fe9c95 100644 --- a/core/java/android/hardware/radio/IRadioService.aidl +++ b/core/java/android/hardware/radio/IRadioService.aidl @@ -31,7 +31,7 @@ interface IRadioService { List listModules(); ITuner openTuner(int moduleId, in RadioManager.BandConfig bandConfig, boolean withAudio, - in ITunerCallback callback); + in ITunerCallback callback, int targetSdkVersion); ICloseHandle addAnnouncementListener(in int[] enabledTypes, in IAnnouncementListener listener); diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 8c6083ce49b63..f072e3b43b4ad 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -1796,7 +1796,7 @@ public class RadioManager { ITuner tuner; TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler); try { - tuner = mService.openTuner(moduleId, config, withAudio, halCallback); + tuner = mService.openTuner(moduleId, config, withAudio, halCallback, mTargetSdkVersion); } catch (RemoteException | IllegalArgumentException | IllegalStateException ex) { Log.e(TAG, "Failed to open tuner", ex); return null; @@ -1873,6 +1873,7 @@ public class RadioManager { @NonNull private final Context mContext; @NonNull private final IRadioService mService; + private final int mTargetSdkVersion; /** * @hide @@ -1889,5 +1890,6 @@ public class RadioManager { public RadioManager(Context context, IRadioService service) { mContext = context; mService = service; + mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; } } diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java index 2b9de18701e2e..87f91fa436ed3 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java @@ -41,6 +41,8 @@ import android.os.Parcel; import android.os.RemoteException; import android.util.ArraySet; +import androidx.test.InstrumentationRegistry; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -54,6 +56,8 @@ import java.util.concurrent.Executor; @RunWith(MockitoJUnitRunner.class) public final class ProgramListTest { + public final Context mContext = InstrumentationRegistry.getContext(); + private static final int CREATOR_ARRAY_SIZE = 3; private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(/* millis= */ 500); @@ -109,8 +113,6 @@ public final class ProgramListTest { @Mock private IRadioService mRadioServiceMock; @Mock - private Context mContextMock; - @Mock private ITuner mTunerMock; @Mock private RadioTuner.Callback mTunerCallbackMock; @@ -477,7 +479,7 @@ public final class ProgramListTest { } private void createRadioTuner() throws Exception { - RadioManager radioManager = new RadioManager(mContextMock, mRadioServiceMock); + RadioManager radioManager = new RadioManager(mContext, mRadioServiceMock); RadioManager.BandConfig band = new RadioManager.FmBandConfig( new RadioManager.FmBandDescriptor(RadioManager.REGION_ITU_1, RadioManager.BAND_FM, /* lowerLimit= */ 87500, /* upperLimit= */ 108000, /* spacing= */ 200, @@ -487,7 +489,7 @@ public final class ProgramListTest { doAnswer(invocation -> { mTunerCallback = (ITunerCallback) invocation.getArguments()[3]; return mTunerMock; - }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any()); + }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any(), anyInt()); mRadioTuner = radioManager.openTuner(/* moduleId= */ 0, band, /* withAudio= */ true, mTunerCallbackMock, /* handler= */ null); diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java index 44aa6d19cb0a2..03742eb93a3bb 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java @@ -18,14 +18,16 @@ package android.hardware.radio.tests.unittests; import static com.google.common.truth.Truth.assertWithMessage; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.hardware.radio.Announcement; import android.hardware.radio.IAnnouncementListener; import android.hardware.radio.ICloseHandle; @@ -34,6 +36,7 @@ import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioMetadata; import android.hardware.radio.RadioTuner; +import android.os.Build; import android.os.Parcel; import android.os.RemoteException; import android.util.ArrayMap; @@ -53,6 +56,8 @@ import java.util.Set; @RunWith(MockitoJUnitRunner.class) public final class RadioManagerTest { + private static final int TEST_TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; + private static final int REGION = RadioManager.REGION_ITU_2; private static final int FM_LOWER_LIMIT = 87500; private static final int FM_UPPER_LIMIT = 108000; @@ -126,6 +131,7 @@ public final class RadioManagerTest { /* vendorInfo= */ new ArrayMap<>())); private RadioManager mRadioManager; + private final ApplicationInfo mApplicationInfo = new ApplicationInfo(); @Mock private IRadioService mRadioServiceMock; @@ -1008,7 +1014,8 @@ public final class RadioManagerTest { mRadioManager.openTuner(moduleId, FM_BAND_CONFIG, withAudio, mCallbackMock, /* handler= */ null); - verify(mRadioServiceMock).openTuner(eq(moduleId), eq(FM_BAND_CONFIG), eq(withAudio), any()); + verify(mRadioServiceMock).openTuner(eq(moduleId), eq(FM_BAND_CONFIG), eq(withAudio), any(), + anyInt()); } @Test @@ -1103,6 +1110,8 @@ public final class RadioManagerTest { } private void createRadioManager() throws RemoteException { + mApplicationInfo.targetSdkVersion = TEST_TARGET_SDK_VERSION; + when(mContextMock.getApplicationInfo()).thenReturn(mApplicationInfo); when(mRadioServiceMock.listModules()).thenReturn(Arrays.asList(AMFM_PROPERTIES)); when(mRadioServiceMock.addAnnouncementListener(any(), any())).thenReturn(mCloseHandleMock); diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java index 6d77f99361964..7a9976779df9f 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.graphics.Bitmap; import android.hardware.radio.IRadioService; import android.hardware.radio.ITuner; @@ -35,6 +36,7 @@ import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioMetadata; import android.hardware.radio.RadioTuner; +import android.os.Build; import org.junit.After; import org.junit.Before; @@ -51,6 +53,8 @@ import java.util.Map; @RunWith(MockitoJUnitRunner.class) public final class TunerAdapterTest { + private static final int TEST_TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; + private static final int CALLBACK_TIMEOUT_MS = 30_000; private static final int AM_LOWER_LIMIT_KHZ = 150; @@ -65,6 +69,7 @@ public final class TunerAdapterTest { private RadioTuner mRadioTuner; private ITunerCallback mTunerCallback; + private final ApplicationInfo mApplicationInfo = new ApplicationInfo(); @Mock private IRadioService mRadioServiceMock; @@ -77,12 +82,14 @@ public final class TunerAdapterTest { @Before public void setUp() throws Exception { + mApplicationInfo.targetSdkVersion = TEST_TARGET_SDK_VERSION; + when(mContextMock.getApplicationInfo()).thenReturn(mApplicationInfo); RadioManager radioManager = new RadioManager(mContextMock, mRadioServiceMock); doAnswer(invocation -> { mTunerCallback = (ITunerCallback) invocation.getArguments()[3]; return mTunerMock; - }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any()); + }).when(mRadioServiceMock).openTuner(anyInt(), any(), anyBoolean(), any(), anyInt()); doAnswer(invocation -> { ProgramSelector program = (ProgramSelector) invocation.getArguments()[0]; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java index a2df426c315c8..9803474566fbf 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceAidlImplTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,6 +34,7 @@ import android.hardware.radio.ICloseHandle; import android.hardware.radio.ITuner; import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; +import android.os.Build; import android.os.IBinder; import android.os.ServiceManager; @@ -55,6 +57,7 @@ public final class IRadioServiceAidlImplTest extends ExtendedRadioMockitoTestCas "android.hardware.broadcastradio.IBroadcastRadio/amfm"; private static final String DAB_SERVICE_NAME = "android.hardware.broadcastradio.IBroadcastRadio/dab"; + private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; private IRadioServiceAidlImpl mAidlImpl; @@ -82,7 +85,7 @@ public final class IRadioServiceAidlImplTest extends ExtendedRadioMockitoTestCas doNothing().when(mServiceMock).enforcePolicyAccess(); when(mHalMock.listModules()).thenReturn(List.of(mModuleMock)); - when(mHalMock.openSession(anyInt(), any(), anyBoolean(), any())) + when(mHalMock.openSession(anyInt(), any(), anyBoolean(), any(), eq(TARGET_SDK_VERSION))) .thenReturn(mTunerMock); when(mHalMock.addAnnouncementListener(any(), any())).thenReturn(mICloseHandle); @@ -114,7 +117,7 @@ public final class IRadioServiceAidlImplTest extends ExtendedRadioMockitoTestCas @Test public void openTuner_forAidlImpl() throws Exception { ITuner tuner = mAidlImpl.openTuner(/* moduleId= */ 0, mBandConfigMock, - /* withAudio= */ true, mTunerCallbackMock); + /* withAudio= */ true, mTunerCallbackMock, TARGET_SDK_VERSION); assertWithMessage("Tuner opened in AIDL HAL") .that(tuner).isEqualTo(mTunerMock); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java index 5ab943542f81d..cfff477362400 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java @@ -32,6 +32,7 @@ import android.hardware.radio.ICloseHandle; import android.hardware.radio.ITuner; import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; +import android.os.Build; import org.junit.Before; import org.junit.Test; @@ -49,6 +50,7 @@ public final class IRadioServiceHidlImplTest { private static final int HAL1_MODULE_ID = 0; private static final int[] ENABLE_TYPES = new int[]{Announcement.TYPE_TRAFFIC}; + private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; private IRadioServiceHidlImpl mHidlImpl; @@ -103,7 +105,7 @@ public final class IRadioServiceHidlImplTest { @Test public void openTuner_withHal1ModuleId_forHidlImpl() throws Exception { ITuner tuner = mHidlImpl.openTuner(HAL1_MODULE_ID, mBandConfigMock, - /* withAudio= */ true, mTunerCallbackMock); + /* withAudio= */ true, mTunerCallbackMock, TARGET_SDK_VERSION); assertWithMessage("Tuner opened in HAL 1") .that(tuner).isEqualTo(mHal1TunerMock); @@ -112,7 +114,7 @@ public final class IRadioServiceHidlImplTest { @Test public void openTuner_withHal2ModuleId_forHidlImpl() throws Exception { ITuner tuner = mHidlImpl.openTuner(HAL1_MODULE_ID + 1, mBandConfigMock, - /* withAudio= */ true, mTunerCallbackMock); + /* withAudio= */ true, mTunerCallbackMock, TARGET_SDK_VERSION); assertWithMessage("Tuner opened in HAL 2") .that(tuner).isEqualTo(mHal2TunerMock); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java index 1cc0a98512bf5..f4040825e9a74 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImplTest.java @@ -36,6 +36,7 @@ import android.hardware.radio.ITuner; import android.hardware.radio.ITunerCallback; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioTuner; +import android.os.Build; import android.os.IBinder; import android.os.IServiceCallback; import android.os.RemoteException; @@ -54,6 +55,8 @@ import java.util.Arrays; public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTestCase { + private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; + private static final int FM_RADIO_MODULE_ID = 0; private static final int DAB_RADIO_MODULE_ID = 1; private static final ArrayList SERVICE_LIST = @@ -137,7 +140,8 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes createBroadcastRadioService(); ITuner session = mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID, - /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock); + /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock, + TARGET_SDK_VERSION); assertWithMessage("Session opened in FM radio module") .that(session).isEqualTo(mFmTunerSessionMock); @@ -148,7 +152,8 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes createBroadcastRadioService(); ITuner session = mBroadcastRadioService.openSession(DAB_RADIO_MODULE_ID + 1, - /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock); + /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock, + TARGET_SDK_VERSION); assertWithMessage("Session opened with id not found").that(session).isNull(); } @@ -160,7 +165,8 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID, - /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock)); + /* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock, + TARGET_SDK_VERSION)); assertWithMessage("Exception for opening session by non-current user") .that(thrown).hasMessageThat().contains("Cannot open session for non-current user"); @@ -228,7 +234,7 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes return null; }).when(mFmBinderMock).linkToDeath(any(), anyInt()); - when(mFmRadioModuleMock.openSession(eq(mTunerCallbackMock))) + when(mFmRadioModuleMock.openSession(eq(mTunerCallbackMock), eq(TARGET_SDK_VERSION))) .thenReturn(mFmTunerSessionMock); } } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java index 31195548c9685..a1cebb630aac3 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java @@ -25,6 +25,7 @@ import android.hardware.broadcastradio.VendorKeyValue; import android.hardware.radio.Announcement; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; +import android.os.Build; import com.google.common.truth.Expect; @@ -68,6 +69,18 @@ public final class ConversionUtilsTest { @Rule public final Expect expect = Expect.create(); + @Test + public void isAtLeastU_withTSdkVersion_returnsFalse() { + expect.withMessage("Target SDK version of T") + .that(ConversionUtils.isAtLeastU(Build.VERSION_CODES.TIRAMISU)).isFalse(); + } + + @Test + public void isAtLeastU_withCurrentSdkVersion_returnsTrue() { + expect.withMessage("Target SDK version of U") + .that(ConversionUtils.isAtLeastU(Build.VERSION_CODES.CUR_DEVELOPMENT)).isTrue(); + } + @Test public void propertiesFromHalProperties_idsMatch() { expect.withMessage("Properties id") diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index b76171aef4663..36c96260ae8f2 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -42,6 +42,7 @@ import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioTuner; +import android.os.Build; import android.os.ServiceSpecificException; import android.util.ArrayMap; import android.util.ArraySet; @@ -65,6 +66,7 @@ import java.util.Map; */ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { + private static final int TARGET_SDK_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT; private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(/* millis= */ 200); private static final int SIGNAL_QUALITY = 1; @@ -298,6 +300,18 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); } + @Test + public void tune_withLowerSdkVersion() throws Exception { + openAidlClients(/* numClients= */ 1, Build.VERSION_CODES.TIRAMISU); + ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + RadioManager.ProgramInfo tuneInfo = + AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); + + mTunerSessions[0].tune(initialSel); + + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + } + @Test public void tune_withMultipleSessions() throws Exception { int numSessions = 3; @@ -627,13 +641,17 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { .onParametersUpdated(parametersExpected); } } - private void openAidlClients(int numClients) throws Exception { + openAidlClients(numClients, TARGET_SDK_VERSION); + } + + private void openAidlClients(int numClients, int targetSdkVersion) throws Exception { mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; mTunerSessions = new TunerSession[numClients]; for (int index = 0; index < numClients; index++) { mAidlTunerCallbackMocks[index] = mock(android.hardware.radio.ITunerCallback.class); - mTunerSessions[index] = mRadioModule.openSession(mAidlTunerCallbackMocks[index]); + mTunerSessions[index] = mRadioModule.openSession(mAidlTunerCallbackMocks[index], + targetSdkVersion); } } diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java index 6a010424db139..b66120d428c3f 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java @@ -86,7 +86,8 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { @Override public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig, - boolean withAudio, ITunerCallback callback) throws RemoteException { + boolean withAudio, ITunerCallback callback, int targetSdkVersion) + throws RemoteException { if (isDebugEnabled()) { Slogf.d(TAG, "Opening module %d", moduleId); } @@ -94,7 +95,7 @@ final class IRadioServiceAidlImpl extends IRadioService.Stub { if (callback == null) { throw new IllegalArgumentException("Callback must not be null"); } - return mHalAidl.openSession(moduleId, bandConfig, withAudio, callback); + return mHalAidl.openSession(moduleId, bandConfig, withAudio, callback, targetSdkVersion); } @Override diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java index 408fba1bff3b1..8a1ba19e24ab7 100644 --- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java +++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java @@ -92,7 +92,8 @@ final class IRadioServiceHidlImpl extends IRadioService.Stub { @Override public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig, - boolean withAudio, ITunerCallback callback) throws RemoteException { + boolean withAudio, ITunerCallback callback, int targetSdkVersion) + throws RemoteException { if (isDebugEnabled()) { Slog.d(TAG, "Opening module " + moduleId); } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java index 03acf72725e7d..772cd4144f11b 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java @@ -199,7 +199,8 @@ public final class BroadcastRadioServiceImpl { */ @Nullable public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig, - boolean withAudio, ITunerCallback callback) throws RemoteException { + boolean withAudio, ITunerCallback callback, int targetSdkVersion) + throws RemoteException { if (DEBUG) { Slogf.d(TAG, "Open AIDL radio session"); } @@ -222,7 +223,7 @@ public final class BroadcastRadioServiceImpl { } } - TunerSession tunerSession = radioModule.openSession(callback); + TunerSession tunerSession = radioModule.openSession(callback, targetSdkVersion); if (legacyConfig != null) { tunerSession.setConfiguration(legacyConfig); } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java index 09ee99659afca..9a7a3b5e18706 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java @@ -33,6 +33,7 @@ import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioMetadata; +import android.os.Build; import android.os.ParcelableException; import android.os.ServiceSpecificException; import android.util.ArrayMap; @@ -62,6 +63,11 @@ final class ConversionUtils { throw new UnsupportedOperationException("ConversionUtils class is noninstantiable"); } + static boolean isAtLeastU(int targetSdkVersion) { + // TODO(b/261770108): Use version code for U. + return targetSdkVersion >= Build.VERSION_CODES.CUR_DEVELOPMENT; + } + static RuntimeException throwOnError(RuntimeException halException, String action) { if (!(halException instanceof ServiceSpecificException)) { return new ParcelableException(new RuntimeException( @@ -472,6 +478,60 @@ final class ConversionUtils { return new ProgramList.Chunk(chunk.purge, chunk.complete, modified, removed); } + private static boolean isNewIdentifierInU(ProgramSelector.Identifier id) { + return id.getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT; + } + + static boolean programSelectorMeetsSdkVersionRequirement(ProgramSelector sel, + int targetSdkVersion) { + if (isAtLeastU(targetSdkVersion)) { + return true; + } + if (sel.getPrimaryId().getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) { + return false; + } + ProgramSelector.Identifier[] secondaryIds = sel.getSecondaryIds(); + for (int i = 0; i < secondaryIds.length; i++) { + if (isNewIdentifierInU(secondaryIds[i])) { + return false; + } + } + return true; + } + + static boolean programInfoMeetsSdkVersionRequirement(RadioManager.ProgramInfo info, + int targetSdkVersion) { + if (isAtLeastU(targetSdkVersion)) { + return true; + } + if (!programSelectorMeetsSdkVersionRequirement(info.getSelector(), targetSdkVersion)) { + return false; + } + if (isNewIdentifierInU(info.getLogicallyTunedTo()) + || isNewIdentifierInU(info.getPhysicallyTunedTo())) { + return false; + } + Iterator relatedContentIt = info.getRelatedContent().iterator(); + while (relatedContentIt.hasNext()) { + if (isNewIdentifierInU(relatedContentIt.next())) { + return false; + } + } + return true; + } + + static ProgramList.Chunk convertChunkToTargetSdkVersion(ProgramList.Chunk chunk, + int targetSdkVersion) { + if (isAtLeastU(targetSdkVersion)) { + return chunk; + } + Set modified = chunk.getModified(); + modified.removeIf(info -> !programInfoMeetsSdkVersionRequirement(info, targetSdkVersion)); + Set removed = chunk.getRemoved(); + removed.removeIf(id -> isNewIdentifierInU(id)); + return new ProgramList.Chunk(chunk.isPurge(), chunk.isComplete(), modified, removed); + } + public static android.hardware.radio.Announcement announcementFromHalAnnouncement( Announcement hwAnnouncement) { return new android.hardware.radio.Announcement( diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java index 5a3bd28cb7047..47203c8854a3a 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java @@ -100,7 +100,15 @@ final class RadioModule { synchronized (mLock) { android.hardware.radio.ProgramSelector csel = ConversionUtils.programSelectorFromHalProgramSelector(programSelector); - fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel)); + fanoutAidlCallbackLocked((cb, sdkVersion) -> { + if (csel != null && !ConversionUtils + .programSelectorMeetsSdkVersionRequirement(csel, sdkVersion)) { + Slogf.e(TAG, "onTuneFailed: cannot send program selector " + + "requiring higher target SDK version"); + return; + } + cb.onTuneFailed(result, csel); + }); } }); } @@ -112,7 +120,13 @@ final class RadioModule { mCurrentProgramInfo = ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo); RadioManager.ProgramInfo currentProgramInfo = mCurrentProgramInfo; - fanoutAidlCallbackLocked(cb -> { + fanoutAidlCallbackLocked((cb, sdkVersion) -> { + if (!ConversionUtils.programInfoMeetsSdkVersionRequirement( + currentProgramInfo, sdkVersion)) { + Slogf.e(TAG, "onCurrentProgramInfoChanged: cannot send " + + "program info requiring higher target SDK version"); + return; + } cb.onCurrentProgramInfoChanged(currentProgramInfo); }); } @@ -139,7 +153,7 @@ final class RadioModule { fireLater(() -> { synchronized (mLock) { mAntennaConnected = connected; - fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected)); + fanoutAidlCallbackLocked((cb, sdkVersion) -> cb.onAntennaState(connected)); } }); } @@ -148,7 +162,9 @@ final class RadioModule { public void onConfigFlagUpdated(int flag, boolean value) { fireLater(() -> { synchronized (mLock) { - fanoutAidlCallbackLocked(cb -> cb.onConfigFlagUpdated(flag, value)); + fanoutAidlCallbackLocked((cb, sdkVersion) -> { + cb.onConfigFlagUpdated(flag, value); + }); } }); } @@ -159,7 +175,9 @@ final class RadioModule { synchronized (mLock) { Map cparam = ConversionUtils.vendorInfoFromHalVendorKeyValues(parameters); - fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam)); + fanoutAidlCallbackLocked((cb, sdkVersion) -> { + cb.onParametersUpdated(cparam); + }); } }); } @@ -223,14 +241,14 @@ final class RadioModule { mService.setTunerCallback(mHalTunerCallback); } - TunerSession openSession(android.hardware.radio.ITunerCallback userCb) + TunerSession openSession(android.hardware.radio.ITunerCallback userCb, int targetSdkVersion) throws RemoteException { mLogger.logRadioEvent("Open TunerSession"); TunerSession tunerSession; Boolean antennaConnected; RadioManager.ProgramInfo currentProgramInfo; synchronized (mLock) { - tunerSession = new TunerSession(this, mService, userCb); + tunerSession = new TunerSession(this, mService, userCb, targetSdkVersion); mAidlTunerSessions.add(tunerSession); antennaConnected = mAntennaConnected; currentProgramInfo = mCurrentProgramInfo; @@ -383,7 +401,8 @@ final class RadioModule { } interface AidlCallbackRunnable { - void run(android.hardware.radio.ITunerCallback callback) throws RemoteException; + void run(android.hardware.radio.ITunerCallback callback, int targetSdkVersion) + throws RemoteException; } // Invokes runnable with each TunerSession currently open. @@ -400,7 +419,8 @@ final class RadioModule { List deadSessions = null; for (int i = 0; i < mAidlTunerSessions.size(); i++) { try { - runnable.run(mAidlTunerSessions.valueAt(i).mCallback); + runnable.run(mAidlTunerSessions.valueAt(i).mCallback, + mAidlTunerSessions.valueAt(i).getTargetSdkVersion()); } catch (DeadObjectException ex) { // The other side died without calling close(), so just purge it from our records. Slogf.e(TAG, "Removing dead TunerSession"); diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java index 66e1be183a0f2..d700ed01b3c35 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java @@ -21,7 +21,6 @@ import android.graphics.Bitmap; import android.hardware.broadcastradio.ConfigFlag; import android.hardware.broadcastradio.IBroadcastRadio; import android.hardware.radio.ITuner; -import android.hardware.radio.ITunerCallback; import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; @@ -47,6 +46,7 @@ final class TunerSession extends ITuner.Stub { private final RadioLogger mLogger; private final RadioModule mModule; final android.hardware.radio.ITunerCallback mCallback; + private final int mTargetSdkVersion; private final IBroadcastRadio mService; @GuardedBy("mLock") @@ -61,10 +61,11 @@ final class TunerSession extends ITuner.Stub { private RadioManager.BandConfig mPlaceHolderConfig; TunerSession(RadioModule radioModule, IBroadcastRadio service, - android.hardware.radio.ITunerCallback callback) { + android.hardware.radio.ITunerCallback callback, int targetSdkVersion) { mModule = Objects.requireNonNull(radioModule, "radioModule cannot be null"); mService = Objects.requireNonNull(service, "service cannot be null"); mCallback = Objects.requireNonNull(callback, "callback cannot be null"); + mTargetSdkVersion = targetSdkVersion; mLogger = new RadioLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE); } @@ -129,7 +130,7 @@ final class TunerSession extends ITuner.Stub { mPlaceHolderConfig = Objects.requireNonNull(config, "config cannot be null"); } Slogf.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL AIDL"); - mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config)); + mModule.fanoutAidlCallback((cb, sdkVersion) -> cb.onConfigurationChanged(config)); } @Override @@ -248,7 +249,9 @@ final class TunerSession extends ITuner.Stub { Slogf.w(TAG, "Cannot start background scan on AIDL HAL client from non-current user"); return false; } - mModule.fanoutAidlCallback(ITunerCallback::onBackgroundScanComplete); + mModule.fanoutAidlCallback((cb, sdkVersion) -> { + cb.onBackgroundScanComplete(); + }); return true; } @@ -276,6 +279,10 @@ final class TunerSession extends ITuner.Stub { mModule.onTunerSessionProgramListFilterChanged(this); } + int getTargetSdkVersion() { + return mTargetSdkVersion; + } + ProgramList.Filter getProgramListFilter() { synchronized (mLock) { return mProgramInfoCache == null ? null : mProgramInfoCache.getFilter(); @@ -311,7 +318,14 @@ final class TunerSession extends ITuner.Stub { } for (int i = 0; i < chunks.size(); i++) { try { - mCallback.onProgramListUpdated(chunks.get(i)); + if (!ConversionUtils.isAtLeastU(getTargetSdkVersion())) { + ProgramList.Chunk downgradedChunk = + ConversionUtils.convertChunkToTargetSdkVersion(chunks.get(i), + getTargetSdkVersion()); + mCallback.onProgramListUpdated(downgradedChunk); + } else { + mCallback.onProgramListUpdated(chunks.get(i)); + } } catch (RemoteException ex) { Slogf.w(TAG, ex, "mCallback.onProgramListUpdated() failed"); } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java index 984bf5125582e..1e31f200fd47d 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java @@ -169,7 +169,7 @@ public final class BroadcastRadioService { } public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig, - boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException { + boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException { Slog.v(TAG, "Open HIDL 2.0 session with module id " + moduleId); if (!RadioServiceUserController.isCurrentOrSystemUser()) { Slogf.e(TAG, "Cannot open tuner on HAL 2.0 client for non-current user"); From 567deade342606c21774f1f2ecbfefae978d5670 Mon Sep 17 00:00:00 2001 From: Weilin Xu Date: Thu, 8 Dec 2022 23:20:00 +0000 Subject: [PATCH 3/3] Implement explicit result conversion for radio HAL Explicit broadcast radio HAL result conversion was implemented so that the order of definition for result enum in HAL does not have to be the same as TunerResultType in RadioTuner. Parameterized tests were also added for this result conversion. Bug: 257337458 Test: atest com.android.server.broadcastradio Test: atest android.hardware.radio.tests.unittests Change-Id: Ia41020e01b0727eea612470bae32810fb8b1cc58 --- .../tests/unittests/TunerAdapterTest.java | 8 +-- .../aidl/ConversionUtilsResultTest.java | 65 ++++++++++++++++++ .../broadcastradio/aidl/TunerSessionTest.java | 2 +- .../hal2/ConvertResultTest.java | 66 +++++++++++++++++++ .../hal2/TunerSessionHidlTest.java | 2 +- .../broadcastradio/aidl/ConversionUtils.java | 22 +++++++ .../broadcastradio/aidl/RadioModule.java | 3 +- .../server/broadcastradio/hal2/Convert.java | 22 +++++++ .../broadcastradio/hal2/RadioModule.java | 3 +- 9 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java create mode 100644 core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertResultTest.java diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java index 7a9976779df9f..d851a7724e8ee 100644 --- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java +++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/TunerAdapterTest.java @@ -98,7 +98,7 @@ public final class TunerAdapterTest { throw new IllegalArgumentException(); } if (program.getPrimaryId().getValue() < AM_LOWER_LIMIT_KHZ) { - mTunerCallback.onTuneFailed(RadioManager.STATUS_BAD_VALUE, program); + mTunerCallback.onTuneFailed(RadioTuner.TUNER_RESULT_INVALID_ARGUMENTS, program); } else { mTunerCallback.onCurrentProgramInfoChanged(FM_PROGRAM_INFO); } @@ -208,14 +208,14 @@ public final class TunerAdapterTest { @Test public void seek_forTunerAdapter_invokesOnErrorWhenTimeout() throws Exception { doAnswer(invocation -> { - mTunerCallback.onTuneFailed(RadioManager.STATUS_TIMED_OUT, FM_SELECTOR); + mTunerCallback.onTuneFailed(RadioTuner.TUNER_RESULT_TIMEOUT, FM_SELECTOR); return RadioManager.STATUS_OK; }).when(mTunerMock).seek(anyBoolean(), anyBoolean()); mRadioTuner.scan(RadioTuner.DIRECTION_UP, /* skipSubChannel*/ true); verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onTuneFailed( - RadioManager.STATUS_TIMED_OUT, FM_SELECTOR); + RadioTuner.TUNER_RESULT_TIMEOUT, FM_SELECTOR); } @Test @@ -246,7 +246,7 @@ public final class TunerAdapterTest { mRadioTuner.tune(invalidSelector); verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)) - .onTuneFailed(RadioManager.STATUS_BAD_VALUE, invalidSelector); + .onTuneFailed(RadioTuner.TUNER_RESULT_INVALID_ARGUMENTS, invalidSelector); } @Test diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java new file mode 100644 index 0000000000000..df3ddfd6bb717 --- /dev/null +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsResultTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 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 com.android.server.broadcastradio.aidl; + +import android.hardware.broadcastradio.Result; +import android.hardware.radio.RadioTuner; + +import com.google.common.truth.Expect; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.List; + +@RunWith(Parameterized.class) +public final class ConversionUtilsResultTest { + + private final int mHalResult; + private final int mTunerResult; + + @Rule + public final Expect expect = Expect.create(); + + public ConversionUtilsResultTest(int halResult, int tunerResult) { + this.mHalResult = halResult; + this.mTunerResult = tunerResult; + } + + @Parameterized.Parameters + public static List inputParameters() { + return Arrays.asList(new Object[][]{ + {Result.OK, RadioTuner.TUNER_RESULT_OK}, + {Result.INTERNAL_ERROR, RadioTuner.TUNER_RESULT_INTERNAL_ERROR}, + {Result.INVALID_ARGUMENTS, RadioTuner.TUNER_RESULT_INVALID_ARGUMENTS}, + {Result.INVALID_STATE, RadioTuner.TUNER_RESULT_INVALID_STATE}, + {Result.NOT_SUPPORTED, RadioTuner.TUNER_RESULT_NOT_SUPPORTED}, + {Result.TIMEOUT, RadioTuner.TUNER_RESULT_TIMEOUT}, + {Result.UNKNOWN_ERROR, RadioTuner.TUNER_RESULT_UNKNOWN_ERROR} + }); + } + + @Test + public void halResultToTunerResult() { + expect.withMessage("Tuner result converted from AIDL HAL result %s", mHalResult) + .that(ConversionUtils.halResultToTunerResult(mHalResult)) + .isEqualTo(mTunerResult); + } +} diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index 36c96260ae8f2..c5c6349df07e4 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -416,7 +416,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { for (int index = 0; index < numSessions; index++) { verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) - .onTuneFailed(eq(Result.TIMEOUT), any()); + .onTuneFailed(eq(RadioTuner.TUNER_RESULT_TIMEOUT), any()); } } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertResultTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertResultTest.java new file mode 100644 index 0000000000000..d106de9be06f2 --- /dev/null +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertResultTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 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 com.android.server.broadcastradio.hal2; + +import android.hardware.broadcastradio.V2_0.Result; +import android.hardware.radio.RadioTuner; + +import com.google.common.truth.Expect; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.List; + +@RunWith(Parameterized.class) +public final class ConvertResultTest { + + private final int mHalResult; + private final int mTunerResult; + + @Rule + public final Expect expect = Expect.create(); + + public ConvertResultTest(int halResult, int tunerResult) { + this.mHalResult = halResult; + this.mTunerResult = tunerResult; + } + + @Parameterized.Parameters + public static List inputParameters() { + return Arrays.asList(new Object[][]{ + {Result.OK, RadioTuner.TUNER_RESULT_OK}, + {Result.INTERNAL_ERROR, RadioTuner.TUNER_RESULT_INTERNAL_ERROR}, + {Result.INVALID_ARGUMENTS, RadioTuner.TUNER_RESULT_INVALID_ARGUMENTS}, + {Result.INVALID_STATE, RadioTuner.TUNER_RESULT_INVALID_STATE}, + {Result.NOT_SUPPORTED, RadioTuner.TUNER_RESULT_NOT_SUPPORTED}, + {Result.TIMEOUT, RadioTuner.TUNER_RESULT_TIMEOUT}, + {Result.UNKNOWN_ERROR, RadioTuner.TUNER_RESULT_UNKNOWN_ERROR} + }); + } + + @Test + public void halResultToTunerResult() { + expect.withMessage("Tuner result converted from HAL 2.0 result %s", + Result.toString(mHalResult)) + .that(Convert.halResultToTunerResult(mHalResult)) + .isEqualTo(mTunerResult); + } +} diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java index 6b75d8d0b5dc6..db16c0362e7c9 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java @@ -409,7 +409,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { for (int index = 0; index < numSessions; index++) { verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) - .onTuneFailed(eq(Result.TIMEOUT), any()); + .onTuneFailed(eq(RadioTuner.TUNER_RESULT_TIMEOUT), any()); } } diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java index 9a7a3b5e18706..3c0fda863cdc2 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java @@ -33,6 +33,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.RadioTuner; import android.os.Build; import android.os.ParcelableException; import android.os.ServiceSpecificException; @@ -95,6 +96,27 @@ final class ConversionUtils { } } + @RadioTuner.TunerResultType + static int halResultToTunerResult(int result) { + switch (result) { + case Result.OK: + return RadioTuner.TUNER_RESULT_OK; + case Result.INTERNAL_ERROR: + return RadioTuner.TUNER_RESULT_INTERNAL_ERROR; + case Result.INVALID_ARGUMENTS: + return RadioTuner.TUNER_RESULT_INVALID_ARGUMENTS; + case Result.INVALID_STATE: + return RadioTuner.TUNER_RESULT_INVALID_STATE; + case Result.NOT_SUPPORTED: + return RadioTuner.TUNER_RESULT_NOT_SUPPORTED; + case Result.TIMEOUT: + return RadioTuner.TUNER_RESULT_TIMEOUT; + case Result.UNKNOWN_ERROR: + default: + return RadioTuner.TUNER_RESULT_UNKNOWN_ERROR; + } + } + static VendorKeyValue[] vendorInfoToHalVendorKeyValues(@Nullable Map info) { if (info == null) { return new VendorKeyValue[]{}; diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java index 47203c8854a3a..6193f234730dd 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java @@ -100,6 +100,7 @@ final class RadioModule { synchronized (mLock) { android.hardware.radio.ProgramSelector csel = ConversionUtils.programSelectorFromHalProgramSelector(programSelector); + int tunerResult = ConversionUtils.halResultToTunerResult(result); fanoutAidlCallbackLocked((cb, sdkVersion) -> { if (csel != null && !ConversionUtils .programSelectorMeetsSdkVersionRequirement(csel, sdkVersion)) { @@ -107,7 +108,7 @@ final class RadioModule { + "requiring higher target SDK version"); return; } - cb.onTuneFailed(result, csel); + cb.onTuneFailed(tunerResult, csel); }); } }); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index 620364dab3b5a..98a450fa45e47 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -36,6 +36,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.RadioTuner; import android.os.ParcelableException; import android.util.Slog; @@ -81,6 +82,27 @@ class Convert { } } + @RadioTuner.TunerResultType + static int halResultToTunerResult(int result) { + switch (result) { + case Result.OK: + return RadioTuner.TUNER_RESULT_OK; + case Result.INTERNAL_ERROR: + return RadioTuner.TUNER_RESULT_INTERNAL_ERROR; + case Result.INVALID_ARGUMENTS: + return RadioTuner.TUNER_RESULT_INVALID_ARGUMENTS; + case Result.INVALID_STATE: + return RadioTuner.TUNER_RESULT_INVALID_STATE; + case Result.NOT_SUPPORTED: + return RadioTuner.TUNER_RESULT_NOT_SUPPORTED; + case Result.TIMEOUT: + return RadioTuner.TUNER_RESULT_TIMEOUT; + case Result.UNKNOWN_ERROR: + default: + return RadioTuner.TUNER_RESULT_UNKNOWN_ERROR; + } + } + static @NonNull ArrayList vendorInfoToHal(@Nullable Map info) { if (info == null) return new ArrayList<>(); diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index 0ea5f0fc1d6a0..59a81544d8ef5 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -87,8 +87,9 @@ final class RadioModule { fireLater(() -> { android.hardware.radio.ProgramSelector csel = Convert.programSelectorFromHal(programSelector); + int tunerResult = Convert.halResultToTunerResult(result); synchronized (mLock) { - fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel)); + fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(tunerResult, csel)); } }); }