Implement tune, cancel and getProgramInformation calls of ITuner.
Test: instrumentation Bug: b/36863239 Change-Id: I7c0faacec7beccddc29f3f207ba6a166cb53e60c
This commit is contained in:
@@ -29,8 +29,6 @@ interface ITuner {
|
||||
|
||||
RadioManager.BandConfig getConfiguration();
|
||||
|
||||
int getProgramInformation(out RadioManager.ProgramInfo[] infoOut);
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if tuner was opened without audio
|
||||
*/
|
||||
@@ -58,4 +56,6 @@ interface ITuner {
|
||||
* @throws IllegalStateException if called out of sequence
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
RadioManager.ProgramInfo getProgramInformation();
|
||||
}
|
||||
|
||||
@@ -137,14 +137,33 @@ class TunerAdapter extends RadioTuner {
|
||||
|
||||
@Override
|
||||
public int tune(int channel, int subChannel) {
|
||||
// TODO(b/36863239): forward to mTuner
|
||||
throw new RuntimeException("Not implemented");
|
||||
try {
|
||||
mTuner.tune(channel, subChannel);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Can't tune", e);
|
||||
return RadioManager.STATUS_INVALID_OPERATION;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Can't tune", e);
|
||||
return RadioManager.STATUS_BAD_VALUE;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "service died", e);
|
||||
return RadioManager.STATUS_DEAD_OBJECT;
|
||||
}
|
||||
return RadioManager.STATUS_OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int cancel() {
|
||||
// TODO(b/36863239): forward to mTuner
|
||||
throw new RuntimeException("Not implemented");
|
||||
try {
|
||||
mTuner.cancel();
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Can't cancel", 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
|
||||
@@ -153,7 +172,8 @@ class TunerAdapter extends RadioTuner {
|
||||
throw new IllegalArgumentException("The argument must be an array of length 1");
|
||||
}
|
||||
try {
|
||||
return mTuner.getProgramInformation(info);
|
||||
info[0] = mTuner.getProgramInformation();
|
||||
return RadioManager.STATUS_OK;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "service died", e);
|
||||
return RadioManager.STATUS_DEAD_OBJECT;
|
||||
|
||||
@@ -61,6 +61,8 @@ class Tuner extends ITuner.Stub {
|
||||
private native void nativeTune(long nativeContext, int channel, int subChannel);
|
||||
private native void nativeCancel(long nativeContext);
|
||||
|
||||
private native RadioManager.ProgramInfo nativeGetProgramInformation(long nativeContext);
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (mLock) {
|
||||
@@ -86,15 +88,6 @@ class Tuner extends ITuner.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMuted(boolean mute) {
|
||||
if (!mWithAudio) {
|
||||
@@ -147,4 +140,11 @@ class Tuner extends ITuner.Stub {
|
||||
nativeCancel(mNativeContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RadioManager.ProgramInfo getProgramInformation() {
|
||||
synchronized (mLock) {
|
||||
return nativeGetProgramInformation(mNativeContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ namespace V1_0 = hardware::broadcastradio::V1_0;
|
||||
namespace V1_1 = hardware::broadcastradio::V1_1;
|
||||
|
||||
using V1_0::BandConfig;
|
||||
using V1_0::ITuner;
|
||||
using V1_0::MetaData;
|
||||
using V1_0::Result;
|
||||
using V1_1::ITunerCallback;
|
||||
@@ -57,7 +56,9 @@ static struct {
|
||||
struct TunerContext {
|
||||
TunerContext() {}
|
||||
|
||||
sp<ITuner> mHalTuner;
|
||||
HalRevision mHalRev;
|
||||
sp<V1_0::ITuner> mHalTuner;
|
||||
sp<V1_1::ITuner> mHalTuner11;
|
||||
sp<TunerCallback> mNativeCallback;
|
||||
|
||||
private:
|
||||
@@ -82,8 +83,8 @@ static jlong nativeInit(JNIEnv *env, jobject obj, jobject clientCallback, jint h
|
||||
AutoMutex _l(gContextMutex);
|
||||
|
||||
auto ctx = new TunerContext();
|
||||
ctx->mNativeCallback = new TunerCallback(env, obj,
|
||||
clientCallback, static_cast<HalRevision>(halRev));
|
||||
ctx->mHalRev = static_cast<HalRevision>(halRev);
|
||||
ctx->mNativeCallback = new TunerCallback(env, obj, clientCallback, ctx->mHalRev);
|
||||
|
||||
static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer");
|
||||
return reinterpret_cast<jlong>(ctx);
|
||||
@@ -97,22 +98,33 @@ static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
void setHalTuner(JNIEnv *env, jobject obj, sp<ITuner> halTuner) {
|
||||
void setHalTuner(JNIEnv *env, jobject obj, sp<V1_0::ITuner> halTuner) {
|
||||
ALOGV("setHalTuner(%p)", halTuner.get());
|
||||
ALOGE_IF(halTuner == nullptr, "HAL tuner is a nullptr");
|
||||
|
||||
AutoMutex _l(gContextMutex);
|
||||
auto& ctx = getNativeContext(env, obj);
|
||||
|
||||
ctx.mHalTuner = halTuner;
|
||||
ctx.mHalTuner11 = V1_1::ITuner::castFrom(halTuner).withDefault(nullptr);
|
||||
ALOGW_IF(ctx.mHalRev >= HalRevision::V1_1 && ctx.mHalTuner11 == nullptr,
|
||||
"Provided tuner does not implement 1.1 HAL");
|
||||
}
|
||||
|
||||
sp<ITuner> getHalTuner(jlong nativeContext) {
|
||||
sp<V1_0::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<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
|
||||
AutoMutex _l(gContextMutex);
|
||||
auto tuner = getNativeContext(nativeContext).mHalTuner11;
|
||||
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);
|
||||
@@ -128,6 +140,7 @@ static void nativeClose(JNIEnv *env, jobject obj, jlong nativeContext) {
|
||||
auto& ctx = getNativeContext(nativeContext);
|
||||
ALOGI("Closing tuner %p", ctx.mHalTuner.get());
|
||||
ctx.mNativeCallback->detach();
|
||||
ctx.mHalTuner11 = nullptr;
|
||||
ctx.mHalTuner = nullptr;
|
||||
ctx.mNativeCallback = nullptr;
|
||||
}
|
||||
@@ -153,7 +166,7 @@ static jobject nativeGetConfiguration(JNIEnv *env, jobject obj, jlong nativeCont
|
||||
halResult = result;
|
||||
halConfig = config;
|
||||
});
|
||||
if (convert::ThrowIfFailed(env, hidlResult)) {
|
||||
if (convert::ThrowIfFailed(env, hidlResult, halResult)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -180,13 +193,44 @@ static void nativeScan(JNIEnv *env, jobject obj, jlong nativeContext,
|
||||
|
||||
static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext,
|
||||
jint channel, jint subChannel) {
|
||||
// TODO(b/36863239): implement
|
||||
jniThrowException(env, "java/lang/RuntimeException", "not implemented yet");
|
||||
ALOGV("nativeTune(%d, %d)", channel, subChannel);
|
||||
auto halTuner = getHalTuner(nativeContext);
|
||||
|
||||
convert::ThrowIfFailed(env, halTuner->tune(channel, subChannel));
|
||||
}
|
||||
|
||||
static void nativeCancel(JNIEnv *env, jobject obj, jlong nativeContext) {
|
||||
// TODO(b/36863239): implement
|
||||
jniThrowException(env, "java/lang/RuntimeException", "not implemented yet");
|
||||
ALOGV("nativeCancel()");
|
||||
auto halTuner = getHalTuner(nativeContext);
|
||||
|
||||
convert::ThrowIfFailed(env, halTuner->cancel());
|
||||
}
|
||||
|
||||
static jobject nativeGetProgramInformation(JNIEnv *env, jobject obj, jlong nativeContext) {
|
||||
ALOGV("nativeGetProgramInformation()");
|
||||
auto halTuner10 = getHalTuner(nativeContext);
|
||||
auto halTuner11 = getHalTuner11(nativeContext);
|
||||
|
||||
V1_1::ProgramInfo halInfo;
|
||||
Result halResult;
|
||||
Return<void> hidlResult;
|
||||
if (halTuner11 != nullptr) {
|
||||
hidlResult = halTuner11->getProgramInformation_1_1([&](Result result,
|
||||
const V1_1::ProgramInfo& info) {
|
||||
halResult = result;
|
||||
halInfo = info;
|
||||
});
|
||||
} else {
|
||||
hidlResult = halTuner10->getProgramInformation([&](Result result,
|
||||
const V1_0::ProgramInfo& info) {
|
||||
halResult = result;
|
||||
halInfo.base = info;
|
||||
});
|
||||
}
|
||||
|
||||
if (convert::ThrowIfFailed(env, hidlResult, halResult)) return nullptr;
|
||||
|
||||
return convert::ProgramInfoFromHal(env, halInfo).release();
|
||||
}
|
||||
|
||||
static const JNINativeMethod gTunerMethods[] = {
|
||||
@@ -201,6 +245,8 @@ static const JNINativeMethod gTunerMethods[] = {
|
||||
{ "nativeScan", "(JZZ)V", (void*)nativeScan },
|
||||
{ "nativeTune", "(JII)V", (void*)nativeTune },
|
||||
{ "nativeCancel", "(J)V", (void*)nativeCancel },
|
||||
{ "nativeGetProgramInformation", "(J)Landroid/hardware/radio/RadioManager$ProgramInfo;",
|
||||
(void*)nativeGetProgramInformation },
|
||||
};
|
||||
|
||||
} // namespace Tuner
|
||||
|
||||
@@ -81,7 +81,7 @@ static struct {
|
||||
} gjni;
|
||||
|
||||
template <typename T>
|
||||
bool ThrowIfFailedCommon(JNIEnv *env, const hardware::Return<T> &hidlResult) {
|
||||
static bool ThrowIfFailedHidl(JNIEnv *env, const Return<T> &hidlResult) {
|
||||
if (hidlResult.isOk()) return false;
|
||||
|
||||
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
|
||||
@@ -89,15 +89,8 @@ bool ThrowIfFailedCommon(JNIEnv *env, const hardware::Return<T> &hidlResult) {
|
||||
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) {
|
||||
static bool ThrowIfFailed(JNIEnv *env, const Result halResult) {
|
||||
switch (halResult) {
|
||||
case Result::OK:
|
||||
return false;
|
||||
case Result::NOT_INITIALIZED:
|
||||
@@ -116,11 +109,20 @@ bool ThrowIfFailed(JNIEnv *env, const hardware::Return<V1_0::Result> &hidlResult
|
||||
return true;
|
||||
default:
|
||||
jniThrowExceptionFmt(env, "java/lang/RuntimeException",
|
||||
"Unknown failure, result: %d", result);
|
||||
"Unknown failure, result: %d", halResult);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ThrowIfFailed(JNIEnv *env, const Return<void> &hidlResult, Result halResult) {
|
||||
return ThrowIfFailedHidl(env, hidlResult) || ThrowIfFailed(env, halResult);
|
||||
}
|
||||
|
||||
bool ThrowIfFailed(JNIEnv *env, const Return<Result> &hidlResult) {
|
||||
return ThrowIfFailedHidl(env, hidlResult)
|
||||
|| ThrowIfFailed(env, static_cast<Result>(hidlResult));
|
||||
}
|
||||
|
||||
static Rds RdsForRegion(bool rds, Region region) {
|
||||
if (!rds) return Rds::NONE;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace V1_0 = hardware::broadcastradio::V1_0;
|
||||
namespace V1_1 = hardware::broadcastradio::V1_1;
|
||||
|
||||
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<V1_0::Result> &hidlResult);
|
||||
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult);
|
||||
bool ThrowIfFailed(JNIEnv *env, const hardware::Return<void> &hidlResult, V1_0::Result halResult);
|
||||
|
||||
JavaRef<jobject> BandConfigFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region);
|
||||
V1_0::BandConfig BandConfigToHal(JNIEnv *env, jobject jConfig, Region ®ion);
|
||||
|
||||
@@ -28,8 +28,10 @@ import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
@@ -38,6 +40,8 @@ import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.after;
|
||||
import static org.mockito.Mockito.atMost;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -52,8 +56,9 @@ public class RadioTest {
|
||||
|
||||
public final Context mContext = InstrumentationRegistry.getContext();
|
||||
|
||||
private final int kConfigCallbacktimeoutNs = 10000;
|
||||
private final int kTuneCallbacktimeoutNs = 30000;
|
||||
private final int kConfigCallbackTimeoutMs = 10000;
|
||||
private final int kCancelTimeoutMs = 1000;
|
||||
private final int kTuneCallbackTimeoutMs = 30000;
|
||||
|
||||
private RadioManager mRadioManager;
|
||||
private RadioTuner mRadioTuner;
|
||||
@@ -124,10 +129,16 @@ public class RadioTest {
|
||||
mRadioTuner = mRadioManager.openTuner(module.getId(),
|
||||
mFmBandConfig, withAudio, mCallback, null);
|
||||
assertNotNull(mRadioTuner);
|
||||
verify(mCallback, timeout(kConfigCallbacktimeoutNs).times(1)).onConfigurationChanged(any());
|
||||
verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
|
||||
resetCallback();
|
||||
}
|
||||
|
||||
private void checkAntenna() {
|
||||
// TODO(b/36863239): enable check when isAntennaConnected is implemented
|
||||
//boolean isConnected = mRadioTuner.isAntennaConnected();
|
||||
//assertTrue(isConnected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOpenTuner() {
|
||||
openTuner();
|
||||
@@ -149,7 +160,7 @@ public class RadioTest {
|
||||
// set
|
||||
int ret = mRadioTuner.setConfiguration(mAmBandConfig);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
verify(mCallback, timeout(kConfigCallbacktimeoutNs).times(1)).onConfigurationChanged(any());
|
||||
verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
|
||||
|
||||
// get
|
||||
RadioManager.BandConfig[] config = new RadioManager.BandConfig[1];
|
||||
@@ -183,7 +194,7 @@ public class RadioTest {
|
||||
// setting good config should recover
|
||||
ret = mRadioTuner.setConfiguration(mAmBandConfig);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
verify(mCallback, timeout(kConfigCallbacktimeoutNs).times(1)).onConfigurationChanged(any());
|
||||
verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -218,15 +229,79 @@ public class RadioTest {
|
||||
@Test
|
||||
public void testStep() {
|
||||
openTuner();
|
||||
checkAntenna();
|
||||
|
||||
int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
verify(mCallback, timeout(kTuneCallbacktimeoutNs).times(1)).onProgramInfoChanged(any());
|
||||
verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
|
||||
|
||||
resetCallback();
|
||||
|
||||
ret = mRadioTuner.step(RadioTuner.DIRECTION_UP, false);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
verify(mCallback, timeout(kTuneCallbacktimeoutNs).times(1)).onProgramInfoChanged(any());
|
||||
verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTuneAndGetPI() {
|
||||
openTuner();
|
||||
checkAntenna();
|
||||
|
||||
int channel = mFmBandConfig.getLowerLimit() + mFmBandConfig.getSpacing();
|
||||
|
||||
// test tune
|
||||
int ret = mRadioTuner.tune(channel, 0);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
ArgumentCaptor<RadioManager.ProgramInfo> infoc =
|
||||
ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
|
||||
verify(mCallback, timeout(kTuneCallbackTimeoutMs))
|
||||
.onProgramInfoChanged(infoc.capture());
|
||||
assertEquals(channel, infoc.getValue().getChannel());
|
||||
|
||||
// test getProgramInformation
|
||||
RadioManager.ProgramInfo[] info = new RadioManager.ProgramInfo[1];
|
||||
ret = mRadioTuner.getProgramInformation(info);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
assertNotNull(info[0]);
|
||||
assertEquals(channel, info[0].getChannel());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDummyCancel() {
|
||||
openTuner();
|
||||
|
||||
int ret = mRadioTuner.cancel();
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLateCancel() {
|
||||
openTuner();
|
||||
checkAntenna();
|
||||
|
||||
int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false);
|
||||
assertEquals(RadioManager.STATUS_OK, ret);
|
||||
verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
|
||||
|
||||
int cancelRet = mRadioTuner.cancel();
|
||||
assertEquals(RadioManager.STATUS_OK, cancelRet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanAndCancel() {
|
||||
openTuner();
|
||||
checkAntenna();
|
||||
|
||||
/* There is a possible race condition between scan and cancel commands - the scan may finish
|
||||
* before cancel command is issued. Thus we accept both outcomes in this test.
|
||||
*/
|
||||
int scanRet = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, true);
|
||||
int cancelRet = mRadioTuner.cancel();
|
||||
|
||||
assertEquals(RadioManager.STATUS_OK, scanRet);
|
||||
assertEquals(RadioManager.STATUS_OK, cancelRet);
|
||||
|
||||
verify(mCallback, after(kCancelTimeoutMs).atMost(1)).onError(RadioTuner.ERROR_CANCELLED);
|
||||
verify(mCallback, atMost(1)).onProgramInfoChanged(any());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user