Implement get/setConfiguration calls of ITuner.
Test: instrumentation Bug: b/36863239 Change-Id: I0954f8f837c342b35873d3ae834bab83bc3cb04c
This commit is contained in:
@@ -22,5 +22,12 @@ import android.hardware.radio.RadioManager;
|
||||
interface ITuner {
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if config is not valid or null
|
||||
*/
|
||||
void setConfiguration(in RadioManager.BandConfig config);
|
||||
|
||||
RadioManager.BandConfig getConfiguration();
|
||||
|
||||
int getProgramInformation(out RadioManager.ProgramInfo[] infoOut);
|
||||
}
|
||||
|
||||
@@ -58,14 +58,28 @@ class TunerAdapter extends RadioTuner {
|
||||
|
||||
@Override
|
||||
public int setConfiguration(RadioManager.BandConfig config) {
|
||||
// TODO(b/36863239): forward to mTuner
|
||||
throw new RuntimeException("Not implemented");
|
||||
try {
|
||||
mTuner.setConfiguration(config);
|
||||
return RadioManager.STATUS_OK;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Can't set configuration", e);
|
||||
return RadioManager.STATUS_BAD_VALUE;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfiguration(RadioManager.BandConfig[] config) {
|
||||
// TODO(b/36863239): forward to mTuner
|
||||
throw new RuntimeException("Not implemented");
|
||||
if (config == null || config.length != 1) {
|
||||
throw new IllegalArgumentException("The argument must be an array of length 1");
|
||||
}
|
||||
try {
|
||||
config[0] = mTuner.getConfiguration();
|
||||
return RadioManager.STATUS_OK;
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,7 +31,8 @@ class Tuner extends ITuner.Stub {
|
||||
*/
|
||||
private final long mNativeContext;
|
||||
|
||||
private int mRegion;
|
||||
private final Object mLock = new Object();
|
||||
private int mRegion; // TODO(b/36863239): find better solution to manage regions
|
||||
|
||||
Tuner(@NonNull ITunerCallback clientCallback, int region) {
|
||||
mRegion = region;
|
||||
@@ -44,17 +45,44 @@ class Tuner extends ITuner.Stub {
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
private native long nativeInit(ITunerCallback clientCallback);
|
||||
private native long nativeInit(@NonNull ITunerCallback clientCallback);
|
||||
private native void nativeFinalize(long nativeContext);
|
||||
private native void nativeClose(long nativeContext);
|
||||
|
||||
private native void nativeSetConfiguration(long nativeContext,
|
||||
@NonNull RadioManager.BandConfig config);
|
||||
private native RadioManager.BandConfig nativeGetConfiguration(long nativeContext, int region);
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
nativeClose(mNativeContext);
|
||||
synchronized (mLock) {
|
||||
nativeClose(mNativeContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(RadioManager.BandConfig config) {
|
||||
if (config == null) {
|
||||
throw new IllegalArgumentException("The argument must not be a null pointer");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
nativeSetConfiguration(mNativeContext, config);
|
||||
mRegion = config.getRegion();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RadioManager.BandConfig getConfiguration() {
|
||||
synchronized (mLock) {
|
||||
return nativeGetConfiguration(mNativeContext, mRegion);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProgramInformation(RadioManager.ProgramInfo[] infoOut) {
|
||||
if (infoOut == null || infoOut.length != 1) {
|
||||
throw new IllegalArgumentException("The argument must be an array of length 1");
|
||||
}
|
||||
Slog.d(TAG, "getProgramInformation()");
|
||||
return RadioManager.STATUS_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "com_android_server_radio_Tuner.h"
|
||||
|
||||
#include "com_android_server_radio_convert.h"
|
||||
#include "com_android_server_radio_Tuner_TunerCallback.h"
|
||||
|
||||
#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
|
||||
@@ -104,6 +105,13 @@ void setHalTuner(JNIEnv *env, jobject obj, sp<ITuner> halTuner) {
|
||||
ctx.mHalTuner = halTuner;
|
||||
}
|
||||
|
||||
sp<ITuner> getHalTuner(jlong nativeContext) {
|
||||
AutoMutex _l(gContextMutex);
|
||||
auto tuner = getNativeContext(nativeContext).mHalTuner;
|
||||
LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner not set");
|
||||
return tuner;
|
||||
}
|
||||
|
||||
sp<ITunerCallback> getNativeCallback(JNIEnv *env, jobject obj) {
|
||||
AutoMutex _l(gContextMutex);
|
||||
auto& ctx = getNativeContext(env, obj);
|
||||
@@ -114,7 +122,7 @@ Region getRegion(JNIEnv *env, jobject obj) {
|
||||
return static_cast<Region>(env->GetIntField(obj, gjni.Tuner.region));
|
||||
}
|
||||
|
||||
static void close(JNIEnv *env, jobject obj, jlong nativeContext) {
|
||||
static void nativeClose(JNIEnv *env, jobject obj, jlong nativeContext) {
|
||||
AutoMutex _l(gContextMutex);
|
||||
auto& ctx = getNativeContext(nativeContext);
|
||||
ALOGI("Closing tuner %p", ctx.mHalTuner.get());
|
||||
@@ -123,10 +131,42 @@ static void close(JNIEnv *env, jobject obj, jlong nativeContext) {
|
||||
ctx.mNativeCallback = nullptr;
|
||||
}
|
||||
|
||||
static void nativeSetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext, jobject config) {
|
||||
ALOGV("nativeSetConfiguration()");
|
||||
auto halTuner = getHalTuner(nativeContext);
|
||||
|
||||
Region region_unused;
|
||||
BandConfig bandConfigHal = convert::BandConfigToHal(env, config, region_unused);
|
||||
|
||||
convert::ThrowIfFailed(env, halTuner->setConfiguration(bandConfigHal));
|
||||
}
|
||||
|
||||
static jobject nativeGetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext,
|
||||
Region region) {
|
||||
ALOGV("nativeSetConfiguration()");
|
||||
auto halTuner = getHalTuner(nativeContext);
|
||||
|
||||
BandConfig halConfig;
|
||||
Result halResult;
|
||||
auto hidlResult = halTuner->getConfiguration([&](Result result, const BandConfig& config) {
|
||||
halResult = result;
|
||||
halConfig = config;
|
||||
});
|
||||
if (convert::ThrowIfFailed(env, hidlResult)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return convert::BandConfigFromHal(env, halConfig, region).release();
|
||||
}
|
||||
|
||||
static const JNINativeMethod gTunerMethods[] = {
|
||||
{ "nativeInit", "(Landroid/hardware/radio/ITunerCallback;)J", (void*)nativeInit },
|
||||
{ "nativeFinalize", "(J)V", (void*)nativeFinalize },
|
||||
{ "nativeClose", "(J)V", (void*)close },
|
||||
{ "nativeClose", "(J)V", (void*)nativeClose },
|
||||
{ "nativeSetConfiguration", "(JLandroid/hardware/radio/RadioManager$BandConfig;)V",
|
||||
(void*)nativeSetConfiguration },
|
||||
{ "nativeGetConfiguration", "(JI)Landroid/hardware/radio/RadioManager$BandConfig;",
|
||||
(void*)nativeGetConfiguration },
|
||||
};
|
||||
|
||||
} // namespace Tuner
|
||||
|
||||
@@ -28,10 +28,12 @@ namespace server {
|
||||
namespace radio {
|
||||
namespace convert {
|
||||
|
||||
using hardware::Return;
|
||||
using hardware::hidl_vec;
|
||||
|
||||
using V1_0::Band;
|
||||
using V1_0::Deemphasis;
|
||||
using V1_0::Result;
|
||||
using V1_0::Rds;
|
||||
|
||||
static struct {
|
||||
@@ -62,6 +64,47 @@ static struct {
|
||||
} BandDescriptor;
|
||||
} gjni;
|
||||
|
||||
template <typename T>
|
||||
bool ThrowIfFailedCommon(JNIEnv *env, const hardware::Return<T> &hidlResult) {
|
||||
if (hidlResult.isOk()) return false;
|
||||
|
||||
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
|
||||
"HIDL call failed: %s", hidlResult.description().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult) {
|
||||
return ThrowIfFailedCommon(env, hidlResult);
|
||||
}
|
||||
|
||||
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<V1_0::Result> &hidlResult) {
|
||||
if (ThrowIfFailedCommon(env, hidlResult)) return true;
|
||||
|
||||
Result result = hidlResult;
|
||||
switch (result) {
|
||||
case Result::OK:
|
||||
return false;
|
||||
case Result::NOT_INITIALIZED:
|
||||
jniThrowException(env, "java/lang/RuntimeException", "Result::NOT_INITIALIZED");
|
||||
return true;
|
||||
case Result::INVALID_ARGUMENTS:
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"Result::INVALID_ARGUMENTS");
|
||||
return true;
|
||||
case Result::INVALID_STATE:
|
||||
jniThrowException(env, "java/lang/IllegalStateException", "Result::INVALID_STATE");
|
||||
return true;
|
||||
case Result::TIMEOUT:
|
||||
jniThrowException(env, "java/lang/RuntimeException",
|
||||
"Result::TIMEOUT (unexpected here)");
|
||||
return true;
|
||||
default:
|
||||
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
|
||||
"Unknown failure, result: %d", result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static Rds RdsForRegion(bool rds, Region region) {
|
||||
if (!rds) return Rds::NONE;
|
||||
|
||||
@@ -95,6 +138,7 @@ static Deemphasis DeemphasisForRegion(Region region) {
|
||||
}
|
||||
|
||||
JavaRef BandConfigFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region) {
|
||||
ALOGV("BandConfigFromHal()");
|
||||
EnvWrapper wrap(env);
|
||||
|
||||
jint spacing = config.spacings.size() > 0 ? config.spacings[0] : 0;
|
||||
@@ -122,6 +166,7 @@ JavaRef BandConfigFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region re
|
||||
}
|
||||
|
||||
V1_0::BandConfig BandConfigToHal(JNIEnv *env, jobject jConfig, Region ®ion) {
|
||||
ALOGV("BandConfigToHal()");
|
||||
auto jDescriptor = env->GetObjectField(jConfig, gjni.BandConfig.descriptor);
|
||||
if (jDescriptor == nullptr) {
|
||||
ALOGE("Descriptor is missing");
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace V1_1 = hardware::broadcastradio::V1_1;
|
||||
JavaRef BandConfigFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region);
|
||||
V1_0::BandConfig BandConfigToHal(JNIEnv *env, jobject jConfig, Region ®ion);
|
||||
|
||||
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<V1_0::Result> &hidlResult);
|
||||
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult);
|
||||
|
||||
} // namespace convert
|
||||
} // namespace radio
|
||||
} // namespace server
|
||||
|
||||
@@ -27,6 +27,18 @@ namespace radio {
|
||||
* frameworks/base/core/java/android/hardware/radio/RadioManager.java.
|
||||
*/
|
||||
|
||||
// Keep in sync with STATUS_* constants from RadioManager.java.
|
||||
enum class Status : jint {
|
||||
OK = 0,
|
||||
ERROR = -0x80000000ll, // Integer.MIN_VALUE
|
||||
PERMISSION_DENIED = -1, // -EPERM
|
||||
NO_INIT = -19, // -ENODEV
|
||||
BAD_VALUE = -22, // -EINVAL
|
||||
DEAD_OBJECT = -32, // -EPIPE
|
||||
INVALID_OPERATION = -38, // -ENOSYS
|
||||
TIMED_OUT = -110, // -ETIMEDOUT
|
||||
};
|
||||
|
||||
// Keep in sync with REGION_* constants from RadioManager.java.
|
||||
enum class Region : jint {
|
||||
ITU_1 = 0,
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.hardware.radio.RadioTuner;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,6 +31,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -37,6 +39,7 @@ import static org.junit.Assume.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -48,6 +51,8 @@ public class RadioTest {
|
||||
|
||||
public final Context mContext = InstrumentationRegistry.getContext();
|
||||
|
||||
private final int kConfigCallbacktimeoutNs = 10000;
|
||||
|
||||
private RadioManager mRadioManager;
|
||||
private RadioTuner mRadioTuner;
|
||||
private final List<RadioManager.ModuleProperties> mModules = new ArrayList<>();
|
||||
@@ -56,6 +61,9 @@ public class RadioTest {
|
||||
RadioManager.AmBandDescriptor mAmBandDescriptor;
|
||||
RadioManager.FmBandDescriptor mFmBandDescriptor;
|
||||
|
||||
RadioManager.BandConfig mAmBandConfig;
|
||||
RadioManager.BandConfig mFmBandConfig;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -98,11 +106,14 @@ public class RadioTest {
|
||||
}
|
||||
assertNotNull(mAmBandDescriptor);
|
||||
assertNotNull(mFmBandDescriptor);
|
||||
RadioManager.BandConfig fmBandConfig =
|
||||
new RadioManager.FmBandConfig.Builder(mFmBandDescriptor).build();
|
||||
mAmBandConfig = new RadioManager.AmBandConfig.Builder(mAmBandDescriptor).build();
|
||||
mFmBandConfig = new RadioManager.FmBandConfig.Builder(mFmBandDescriptor).build();
|
||||
|
||||
mRadioTuner = mRadioManager.openTuner(module.getId(), fmBandConfig, true, mCallback, null);
|
||||
mRadioTuner = mRadioManager.openTuner(module.getId(), mFmBandConfig, true, mCallback, null);
|
||||
assertNotNull(mRadioTuner);
|
||||
verify(mCallback, timeout(kConfigCallbacktimeoutNs).times(1)).onConfigurationChanged(any());
|
||||
verify(mCallback, never()).onError(anyInt());
|
||||
Mockito.reset(mCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,26 +123,59 @@ public class RadioTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReopenTuner() {
|
||||
public void testReopenTuner() throws Throwable {
|
||||
openTuner();
|
||||
mRadioTuner.close();
|
||||
mRadioTuner = null;
|
||||
Thread.sleep(100); // TODO(b/36122635): force reopen
|
||||
openTuner();
|
||||
verify(mCallback, never()).onError(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@org.junit.Ignore("setConfiguration is not implemented yet")
|
||||
public void testSetAndGetConfiguration() {
|
||||
openTuner();
|
||||
|
||||
RadioManager.BandConfig amBandConfig =
|
||||
new RadioManager.AmBandConfig.Builder(mAmBandDescriptor).build();
|
||||
mRadioTuner.setConfiguration(amBandConfig);
|
||||
// set
|
||||
int ret = mRadioTuner.setConfiguration(mAmBandConfig);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
verify(mCallback, timeout(kConfigCallbacktimeoutNs).times(1)).onConfigurationChanged(any());
|
||||
|
||||
// get
|
||||
RadioManager.BandConfig[] config = new RadioManager.BandConfig[1];
|
||||
ret = mRadioTuner.getConfiguration(config);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
|
||||
verify(mCallback, times(1)).onConfigurationChanged(any());
|
||||
verify(mCallback, never()).onError(anyInt());
|
||||
assertEquals(mAmBandConfig, config[0]);
|
||||
}
|
||||
|
||||
// TODO(b/36863239): implement "get" too
|
||||
@Test
|
||||
public void testSetBadConfiguration() throws Throwable {
|
||||
openTuner();
|
||||
|
||||
// set bad config
|
||||
Constructor<RadioManager.AmBandConfig> configConstr =
|
||||
RadioManager.AmBandConfig.class.getDeclaredConstructor(
|
||||
int.class, int.class, int.class, int.class, int.class, boolean.class);
|
||||
configConstr.setAccessible(true);
|
||||
RadioManager.AmBandConfig badConfig = configConstr.newInstance(
|
||||
0 /*region*/, RadioManager.BAND_AM /*type*/,
|
||||
10000 /*lowerLimit*/, 1 /*upperLimit*/, 100 /*spacing*/, false /*stereo*/);
|
||||
int ret = mRadioTuner.setConfiguration(badConfig);
|
||||
assertEquals(RadioManager.STATUS_BAD_VALUE, ret);
|
||||
verify(mCallback, never()).onConfigurationChanged(any());
|
||||
|
||||
// set null config
|
||||
ret = mRadioTuner.setConfiguration(null);
|
||||
assertEquals(RadioManager.STATUS_BAD_VALUE, ret);
|
||||
verify(mCallback, never()).onConfigurationChanged(any());
|
||||
|
||||
// setting good config should recover
|
||||
ret = mRadioTuner.setConfiguration(mAmBandConfig);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
verify(mCallback, timeout(kConfigCallbacktimeoutNs).times(1)).onConfigurationChanged(any());
|
||||
|
||||
verify(mCallback, never()).onError(anyInt());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user