Merge "Implement tune, cancel and getProgramInformation calls of ITuner."

This commit is contained in:
Tomasz Wasilczyk
2017-05-10 15:15:14 +00:00
committed by Android (Google) Code Review
7 changed files with 189 additions and 46 deletions

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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 &region);

View File

@@ -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());
}
}