diff options
| author | 2017-07-17 13:57:12 -0700 | |
|---|---|---|
| committer | 2017-07-19 11:20:06 -0700 | |
| commit | 4482b1413656b9c0c5937b4d7a73235f872a6678 (patch) | |
| tree | ab208b2bfd66c2f5e8eb2d0dd6b90586110209d9 | |
| parent | c05ea28fc5b6d09f79f7fdedb27a84c661d0784c (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.txt | 2 | ||||
| -rw-r--r-- | core/java/android/hardware/radio/ITuner.aidl | 3 | ||||
| -rw-r--r-- | core/java/android/hardware/radio/RadioMetadata.java | 51 | ||||
| -rw-r--r-- | core/java/android/hardware/radio/RadioTuner.java | 22 | ||||
| -rw-r--r-- | core/java/android/hardware/radio/TunerAdapter.java | 10 | ||||
| -rw-r--r-- | services/core/java/com/android/server/radio/Tuner.java | 21 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_radio_RadioService.cpp | 2 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_radio_Tuner.cpp | 47 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_radio_Tuner.h | 4 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_radio_convert.cpp | 5 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_radio_convert.h | 3 |
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 |