summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tomasz Wasilczyk <twasilczyk@google.com> 2017-07-17 13:57:12 -0700
committer Tomasz Wasilczyk <twasilczyk@google.com> 2017-07-19 11:20:06 -0700
commit4482b1413656b9c0c5937b4d7a73235f872a6678 (patch)
treeab208b2bfd66c2f5e8eb2d0dd6b90586110209d9
parentc05ea28fc5b6d09f79f7fdedb27a84c661d0784c (diff)
Implement out-of-band metadata images.
This saves a lot of HIDL bandwidth, by not including raw image data in metadata vector. Bug: b/63702941 Test: instumentalization, none added Change-Id: I4aa3df126e4e9ab1821d98ea91d4badec5a2cf82
-rw-r--r--api/system-current.txt2
-rw-r--r--core/java/android/hardware/radio/ITuner.aidl3
-rw-r--r--core/java/android/hardware/radio/RadioMetadata.java51
-rw-r--r--core/java/android/hardware/radio/RadioTuner.java22
-rw-r--r--core/java/android/hardware/radio/TunerAdapter.java10
-rw-r--r--services/core/java/com/android/server/radio/Tuner.java21
-rw-r--r--services/core/jni/com_android_server_radio_RadioService.cpp2
-rw-r--r--services/core/jni/com_android_server_radio_Tuner.cpp47
-rw-r--r--services/core/jni/com_android_server_radio_Tuner.h4
-rw-r--r--services/core/jni/com_android_server_radio_convert.cpp5
-rw-r--r--services/core/jni/com_android_server_radio_convert.h3
11 files changed, 156 insertions, 14 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index e34afc5d7a08..a7167a9d76ee 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -17415,7 +17415,7 @@ package android.hardware.radio {
public final class RadioMetadata implements android.os.Parcelable {
method public boolean containsKey(java.lang.String);
method public int describeContents();
- method public android.graphics.Bitmap getBitmap(java.lang.String);
+ method public deprecated android.graphics.Bitmap getBitmap(java.lang.String);
method public android.hardware.radio.RadioMetadata.Clock getClock(java.lang.String);
method public int getInt(java.lang.String);
method public java.lang.String getString(java.lang.String);
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 2dc66012c446..7f470ef15b96 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -16,6 +16,7 @@
package android.hardware.radio;
+import android.graphics.Bitmap;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
@@ -64,6 +65,8 @@ interface ITuner {
RadioManager.ProgramInfo getProgramInformation();
+ Bitmap getImage(int id);
+
/**
* @returns {@code true} if the scan was properly scheduled,
* {@code false} if the scan feature is unavailable
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index d11e8b0a7274..fba29cd21324 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -15,6 +15,7 @@
*/
package android.hardware.radio;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -240,6 +241,14 @@ public final class RadioMetadata implements Parcelable {
return mBundle.getString(key);
}
+ private static void putInt(Bundle bundle, String key, int value) {
+ int type = METADATA_KEYS_TYPE.getOrDefault(key, METADATA_TYPE_INVALID);
+ if (type != METADATA_TYPE_INT && type != METADATA_TYPE_BITMAP) {
+ throw new IllegalArgumentException("The " + key + " key cannot be used to put an int");
+ }
+ bundle.putInt(key, value);
+ }
+
/**
* Returns the value associated with the given key,
* or 0 if the key is not found in the meta data.
@@ -256,7 +265,9 @@ public final class RadioMetadata implements Parcelable {
*
* @param key The key the value is stored under
* @return a {@link Bitmap} or null
+ * @deprecated Use getBitmapId(String) instead
*/
+ @Deprecated
public Bitmap getBitmap(String key) {
Bitmap bmp = null;
try {
@@ -268,6 +279,30 @@ public final class RadioMetadata implements Parcelable {
return bmp;
}
+ /**
+ * Retrieves an identifier for a bitmap.
+ *
+ * The format of an identifier is opaque to the application,
+ * with a special case of value 0 being invalid.
+ * An identifier for a given image-tuner pair is unique, so an application
+ * may cache images and determine if there is a necessity to fetch them
+ * again - if identifier changes, it means the image has changed.
+ *
+ * Only bitmap keys may be used with this method:
+ * <ul>
+ * <li>{@link #METADATA_KEY_ICON}</li>
+ * <li>{@link #METADATA_KEY_ART}</li>
+ * </ul>
+ *
+ * @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
+ */
+ public int getBitmapId(@NonNull String key) {
+ if (!METADATA_KEY_ICON.equals(key) && !METADATA_KEY_ART.equals(key)) return 0;
+ return getInt(key);
+ }
+
public Clock getClock(String key) {
Clock clock = null;
try {
@@ -416,18 +451,14 @@ public final class RadioMetadata implements Parcelable {
* <li>{@link #METADATA_KEY_RDS_PTY}</li>
* <li>{@link #METADATA_KEY_RBDS_PTY}</li>
* </ul>
+ * or any bitmap represented by its identifier.
*
* @param key The key for referencing this value
* @param value The int value to store
* @return the same Builder instance
*/
public Builder putInt(String key, int value) {
- if (!METADATA_KEYS_TYPE.containsKey(key) ||
- METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_INT) {
- throw new IllegalArgumentException("The " + key
- + " key cannot be used to put a long");
- }
- mBundle.putInt(key, value);
+ RadioMetadata.putInt(mBundle, key, value);
return this;
}
@@ -498,12 +529,12 @@ public final class RadioMetadata implements Parcelable {
int putIntFromNative(int nativeKey, int value) {
String key = getKeyFromNativeKey(nativeKey);
- if (!METADATA_KEYS_TYPE.containsKey(key) ||
- METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_INT) {
+ try {
+ putInt(mBundle, key, value);
+ return 0;
+ } catch (IllegalArgumentException ex) {
return -1;
}
- mBundle.putInt(key, value);
- return 0;
}
int putStringFromNative(int nativeKey, String value) {
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 6637ed2f2448..be37cd0e342b 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -19,6 +19,7 @@ package android.hardware.radio;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.graphics.Bitmap;
import android.os.Handler;
import java.util.List;
@@ -230,6 +231,27 @@ public abstract class RadioTuner {
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
+ * on the application side.
+ *
+ * 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.
+ *
+ * @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
+ */
+ 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.
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index 3bc0ac32f904..d1eff5e04725 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -18,6 +18,7 @@ package android.hardware.radio;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Bitmap;
import android.os.RemoteException;
import android.util.Log;
@@ -203,6 +204,15 @@ class TunerAdapter extends RadioTuner {
}
@Override
+ public @Nullable Bitmap getMetadataImage(int id) {
+ try {
+ return mTuner.getImage(id);
+ } catch (RemoteException e) {
+ throw new RuntimeException("service died", e);
+ }
+ }
+
+ @Override
public boolean startBackgroundScan() {
try {
return mTuner.startBackgroundScan();
diff --git a/services/core/java/com/android/server/radio/Tuner.java b/services/core/java/com/android/server/radio/Tuner.java
index 3d16cacef2c3..4d13adb436a2 100644
--- a/services/core/java/com/android/server/radio/Tuner.java
+++ b/services/core/java/com/android/server/radio/Tuner.java
@@ -17,6 +17,8 @@
package com.android.server.radio;
import android.annotation.NonNull;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.ProgramSelector;
@@ -86,6 +88,8 @@ class Tuner extends ITuner.Stub {
private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
String filter);
+ private native byte[] nativeGetImage(long nativeContext, int id);
+
private native boolean nativeIsAnalogForced(long nativeContext);
private native void nativeSetAnalogForced(long nativeContext, boolean isForced);
@@ -213,6 +217,23 @@ class Tuner extends ITuner.Stub {
}
@Override
+ public Bitmap getImage(int id) {
+ if (id == 0) {
+ throw new IllegalArgumentException("Image ID is missing");
+ }
+
+ byte[] rawImage;
+ synchronized (mLock) {
+ rawImage = nativeGetImage(mNativeContext, id);
+ }
+ if (rawImage == null || rawImage.length == 0) {
+ return null;
+ }
+
+ return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
+ }
+
+ @Override
public boolean startBackgroundScan() {
synchronized (mLock) {
checkNotClosedLocked();
diff --git a/services/core/jni/com_android_server_radio_RadioService.cpp b/services/core/jni/com_android_server_radio_RadioService.cpp
index 459c0d05ca77..b06243da7c5b 100644
--- a/services/core/jni/com_android_server_radio_RadioService.cpp
+++ b/services/core/jni/com_android_server_radio_RadioService.cpp
@@ -232,7 +232,7 @@ static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jin
return nullptr;
}
- Tuner::setHalTuner(env, tuner, halTuner);
+ Tuner::assignHalInterfaces(env, tuner, module, halTuner);
ALOGD("Opened tuner %p", halTuner.get());
return tuner.release();
}
diff --git a/services/core/jni/com_android_server_radio_Tuner.cpp b/services/core/jni/com_android_server_radio_Tuner.cpp
index d984a0cffa50..96da038b3cf6 100644
--- a/services/core/jni/com_android_server_radio_Tuner.cpp
+++ b/services/core/jni/com_android_server_radio_Tuner.cpp
@@ -82,6 +82,8 @@ struct TunerContext {
HalRevision mHalRev;
bool mWithAudio;
Band mBand;
+ wp<V1_0::IBroadcastRadio> mHalModule;
+ wp<V1_1::IBroadcastRadio> mHalModule11;
sp<V1_0::ITuner> mHalTuner;
sp<V1_1::ITuner> mHalTuner11;
sp<HalDeathRecipient> mHalDeathRecipient;
@@ -146,7 +148,8 @@ static void notifyAudioService(TunerContext& ctx, bool connected) {
IPCThreadState::self()->restoreCallingIdentity(token);
}
-void setHalTuner(JNIEnv *env, JavaRef<jobject> const &jTuner, sp<V1_0::ITuner> halTuner) {
+void assignHalInterfaces(JNIEnv *env, JavaRef<jobject> const &jTuner,
+ sp<V1_0::IBroadcastRadio> halModule, sp<V1_0::ITuner> halTuner) {
ALOGV("setHalTuner(%p)", halTuner.get());
ALOGE_IF(halTuner == nullptr, "HAL tuner is a nullptr");
@@ -163,6 +166,9 @@ void setHalTuner(JNIEnv *env, JavaRef<jobject> const &jTuner, sp<V1_0::ITuner> h
return;
}
+ ctx.mHalModule = halModule;
+ ctx.mHalModule11 = V1_1::IBroadcastRadio::castFrom(halModule).withDefault(nullptr);
+
ctx.mHalTuner = halTuner;
ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr);
ALOGW_IF(ctx.mHalRev >= HalRevision::V1_1 && ctx.mHalTuner11 == nullptr,
@@ -390,6 +396,44 @@ static jobject nativeGetProgramList(JNIEnv *env, jobject obj, jlong nativeContex
return jList.release();
}
+static jbyteArray nativeGetImage(JNIEnv *env, jobject obj, jlong nativeContext, jint id) {
+ ALOGV("%s(%x)", __func__, id);
+ AutoMutex _l(gContextMutex);
+ auto& ctx = getNativeContext(nativeContext);
+
+ if (ctx.mHalModule11 == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Out-of-band images are not supported with HAL < 1.1");
+ return nullptr;
+ }
+
+ auto halModule = ctx.mHalModule11.promote();
+ if (halModule == nullptr) {
+ ALOGE("HAL module is gone");
+ return nullptr;
+ }
+
+ JavaRef<jbyteArray> jRawImage = nullptr;
+
+ auto hidlResult = halModule->getImage(id, [&](hidl_vec<uint8_t> rawImage) {
+ auto len = rawImage.size();
+ if (len == 0) return;
+
+ jRawImage = make_javaref(env, env->NewByteArray(len));
+ if (jRawImage == nullptr) {
+ ALOGE("Failed to allocate byte array of len %zu", len);
+ return;
+ }
+
+ env->SetByteArrayRegion(jRawImage.get(), 0, len,
+ reinterpret_cast<const jbyte*>(rawImage.data()));
+ });
+
+ if (convert::ThrowIfFailed(env, hidlResult)) return nullptr;
+
+ return jRawImage.get();
+}
+
static bool nativeIsAnalogForced(JNIEnv *env, jobject obj, jlong nativeContext) {
ALOGV("nativeIsAnalogForced()");
auto halTuner = getHalTuner11(nativeContext);
@@ -457,6 +501,7 @@ static const JNINativeMethod gTunerMethods[] = {
{ "nativeStartBackgroundScan", "(J)Z", (void*)nativeStartBackgroundScan },
{ "nativeGetProgramList", "(JLjava/lang/String;)Ljava/util/List;",
(void*)nativeGetProgramList },
+ { "nativeGetImage", "(JI)[B", (void*)nativeGetImage},
{ "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced },
{ "nativeSetAnalogForced", "(JZ)V", (void*)nativeSetAnalogForced },
{ "nativeIsAntennaConnected", "(J)Z", (void*)nativeIsAntennaConnected },
diff --git a/services/core/jni/com_android_server_radio_Tuner.h b/services/core/jni/com_android_server_radio_Tuner.h
index f653832c18e9..7c100e88ff13 100644
--- a/services/core/jni/com_android_server_radio_Tuner.h
+++ b/services/core/jni/com_android_server_radio_Tuner.h
@@ -21,6 +21,7 @@
#include "JavaRef.h"
+#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/1.1/ITuner.h>
#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
#include <jni.h>
@@ -34,7 +35,8 @@ namespace server {
namespace radio {
namespace Tuner {
-void setHalTuner(JNIEnv *env, JavaRef<jobject> const &jTuner,
+void assignHalInterfaces(JNIEnv *env, JavaRef<jobject> const &jTuner,
+ sp<hardware::broadcastradio::V1_0::IBroadcastRadio> halModule,
sp<hardware::broadcastradio::V1_0::ITuner> halTuner);
sp<hardware::broadcastradio::V1_1::ITunerCallback>
diff --git a/services/core/jni/com_android_server_radio_convert.cpp b/services/core/jni/com_android_server_radio_convert.cpp
index 6cad7f8adc55..767c04a9d3e3 100644
--- a/services/core/jni/com_android_server_radio_convert.cpp
+++ b/services/core/jni/com_android_server_radio_convert.cpp
@@ -126,6 +126,11 @@ static struct {
} ParcelableException;
} gjni;
+template <>
+bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult) {
+ return __ThrowIfFailedHidl(env, hidlResult);
+}
+
bool __ThrowIfFailedHidl(JNIEnv *env, const hardware::details::return_status &hidlResult) {
if (hidlResult.isOk()) return false;
diff --git a/services/core/jni/com_android_server_radio_convert.h b/services/core/jni/com_android_server_radio_convert.h
index 8b91ced92332..3d1b8c5234ff 100644
--- a/services/core/jni/com_android_server_radio_convert.h
+++ b/services/core/jni/com_android_server_radio_convert.h
@@ -68,6 +68,9 @@ bool ThrowIfFailed(JNIEnv *env, const hardware::Return<T> &hidlResult) {
return __ThrowIfFailedHidl(env, hidlResult) || __ThrowIfFailed(env, static_cast<T>(hidlResult));
}
+template <>
+bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult);
+
} // namespace convert
} // namespace radio
} // namespace server