Expose radio metadata image query APIs as system APIs
Promote hidden APIs related to radio metadata image query,
getBitmapId and getMetadata, to system API.
Bug: 318362428
Test: atest CtsBroadcastRadioTestCases
Change-Id: I5187cf717467a7f9093ed121df52c89a9414acdf
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ea008ac..6a1df16 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -6124,6 +6124,7 @@
method public boolean containsKey(String);
method public int describeContents();
method @Deprecated public android.graphics.Bitmap getBitmap(String);
+ method @FlaggedApi("android.hardware.radio.hd_radio_improved") public int getBitmapId(@NonNull String);
method public android.hardware.radio.RadioMetadata.Clock getClock(String);
method public int getInt(String);
method public String getString(String);
@@ -6187,6 +6188,7 @@
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 @FlaggedApi("android.hardware.radio.hd_radio_improved") @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public android.graphics.Bitmap getMetadataImage(int);
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<java.lang.String,java.lang.String> getParameters(@NonNull java.util.List<java.lang.String>);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RADIO) public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]);
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index db14c08..da6b9c2 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -507,10 +507,16 @@
*
* @param key The key the value is stored under.
* @return a bitmap identifier or 0 if it's missing.
- * @hide This API is not thoroughly elaborated yet
+ * @throws NullPointerException if metadata key is {@code null}
+ * @throws IllegalArgumentException if the metadata with the key is not found in
+ * metadata or the key is not of bitmap-key type
*/
+ @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
public int getBitmapId(@NonNull String key) {
- if (!METADATA_KEY_ICON.equals(key) && !METADATA_KEY_ART.equals(key)) return 0;
+ Objects.requireNonNull(key, "Metadata key can not be null");
+ if (!METADATA_KEY_ICON.equals(key) && !METADATA_KEY_ART.equals(key)) {
+ throw new IllegalArgumentException("Failed to retrieve key " + key + " as bitmap key");
+ }
return getInt(key);
}
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 9b2bcde..7c5c003 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -17,6 +17,7 @@
package android.hardware.radio;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -332,29 +333,30 @@
public abstract int getProgramInformation(RadioManager.ProgramInfo[] info);
/**
- * Retrieves a {@link Bitmap} for the given image ID or null,
+ * Retrieves a {@link Bitmap} for the given image ID or throw {@link IllegalArgumentException},
* if the image was missing from the tuner.
*
* <p>This involves doing a call to the tuner, so the bitmap should be cached
* on the application side.
*
- * <p>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
+ * <p>If the method throws {@link IllegalArgumentException} for non-zero ID, it
+ * means the image was updated on the tuner side. There is a race condition 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 Callback#onProgramInfoChanged(RadioManager.ProgramInfo)}
* callback.
*
* @param id The image identifier, retrieved with
* {@link RadioMetadata#getBitmapId(String)}.
- * @return A {@link Bitmap} or null.
- * @throws IllegalArgumentException if id==0
- * @hide This API is not thoroughly elaborated yet
+ * @return A {@link Bitmap} for the given image ID.
+ * @throws IllegalArgumentException if id is 0 or the referenced image id no longer exists.
*/
- @SuppressWarnings("HiddenAbstractMethod")
+ @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
@RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
- public abstract @Nullable Bitmap getMetadataImage(int id);
-
+ public @NonNull Bitmap getMetadataImage(int id) {
+ throw new UnsupportedOperationException(
+ "Getting metadata image must be implemented in child classes");
+ }
/**
* Initiates a background scan to update internally cached program list.
*
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index ba31ca3..63b2d4c 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -16,6 +16,7 @@
package android.hardware.radio;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.os.RemoteException;
@@ -251,10 +252,18 @@
}
@Override
- @Nullable
+ @NonNull
public Bitmap getMetadataImage(int id) {
+ if (id == 0) {
+ throw new IllegalArgumentException("Invalid metadata image id 0");
+ }
try {
- return mTuner.getImage(id);
+ Bitmap bitmap = mTuner.getImage(id);
+ if (bitmap == null) {
+ throw new IllegalArgumentException("Metadata image with id " + id
+ + " is not available");
+ }
+ return bitmap;
} catch (RemoteException e) {
throw new RuntimeException("Service died", e);
}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/DefaultRadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/DefaultRadioTunerTest.java
index 63de759..ddf3615 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/DefaultRadioTunerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/DefaultRadioTunerTest.java
@@ -20,8 +20,6 @@
import static org.junit.Assert.assertThrows;
-import android.graphics.Bitmap;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -88,12 +86,6 @@
return 0;
}
- @Nullable
- @Override
- public Bitmap getMetadataImage(int id) {
- return null;
- }
-
@Override
public boolean startBackgroundScan() {
return false;
@@ -138,6 +130,16 @@
}
@Test
+ public void getMetadataImage_forRadioTuner_throwsException() {
+ UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class,
+ () -> DEFAULT_RADIO_TUNER.getMetadataImage(/* id= */ 1));
+
+ assertWithMessage("Exception for getting metadata image from default radio tuner")
+ .that(thrown).hasMessageThat()
+ .contains("Getting metadata image must be implemented in child classes");
+ }
+
+ @Test
public void getDynamicProgramList_forRadioTuner_returnsNull() {
assertWithMessage("Dynamic program list obtained from default radio tuner")
.that(DEFAULT_RADIO_TUNER.getDynamicProgramList(new ProgramList.Filter())).isNull();
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
index fddfd39..b3a0aba 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioMetadataTest.java
@@ -290,13 +290,29 @@
}
@Test
- public void getBitmapId_withIllegalKey() {
+ public void getBitmapId_withIllegalKey_fails() {
String illegalKey = RadioMetadata.METADATA_KEY_ARTIST;
RadioMetadata metadata = mBuilder.putInt(RadioMetadata.METADATA_KEY_ART, INT_KEY_VALUE)
.build();
- mExpect.withMessage("Bitmap id value with non-bitmap-id-type key %s", illegalKey)
- .that(metadata.getBitmapId(illegalKey)).isEqualTo(0);
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
+ metadata.getBitmapId(illegalKey);
+ });
+
+ mExpect.withMessage("Exception for getting string array for non-bitmap-id type key %s",
+ illegalKey).that(thrown).hasMessageThat().contains("bitmap key");
+ }
+
+ @Test
+ public void getBitmapId_withNullKey_fails() {
+ RadioMetadata metadata = mBuilder.build();
+
+ NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
+ metadata.getBitmapId(/* key= */ null);
+ });
+
+ mExpect.withMessage("Exception for getting bitmap identifier with null key")
+ .that(thrown).hasMessageThat().contains("can not be null");
}
@Test
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
index 4cda26d..5aace81 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/TunerAdapterTest.java
@@ -514,6 +514,26 @@
}
@Test
+ public void getMetadataImage_withImageIdUnavailable_fails() throws Exception {
+ int nonExistImageId = 2;
+ when(mTunerMock.getImage(nonExistImageId)).thenReturn(null);
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mRadioTuner.getMetadataImage(nonExistImageId));
+
+ assertWithMessage("Exception for getting metadata image with non-existing id")
+ .that(thrown).hasMessageThat().contains("is not available");
+ }
+
+ @Test
+ public void getMetadataImage_withInvalidId_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mRadioTuner.getMetadataImage(/* id= */ 0));
+
+ assertWithMessage("Exception for getting metadata image for id 0").that(thrown)
+ .hasMessageThat().contains("Invalid metadata image id 0");
+ }
+
+ @Test
public void getMetadataImage_whenServiceDied_fails() throws Exception {
when(mTunerMock.getImage(anyInt())).thenThrow(new RemoteException());