Make RadioService actually talking to the HAL.
For now, only opening and closing a tuner is implemented.
Test: instrumentation, KitchenSink
Bug: b/36863239
Change-Id: Ib2e14c0108c0714524d50b9557f24465c68f5ef2
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 23637de..3e167e4 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -18,6 +18,8 @@
$(LOCAL_REL_DIR)/com_android_server_location_ContextHubService.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_radio_RadioService.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_radio_Tuner.cpp \
$(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SyntheticPasswordManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_storage_AppFuseBridge.cpp \
@@ -80,6 +82,8 @@
libutils \
libhwui \
android.hardware.audio.common@2.0 \
+ android.hardware.broadcastradio@1.0 \
+ android.hardware.broadcastradio@1.1 \
android.hardware.contexthub@1.0 \
android.hardware.gnss@1.0 \
android.hardware.ir@1.0 \
diff --git a/services/core/jni/com_android_server_radio_RadioService.cpp b/services/core/jni/com_android_server_radio_RadioService.cpp
new file mode 100644
index 0000000..34dbf0c
--- /dev/null
+++ b/services/core/jni/com_android_server_radio_RadioService.cpp
@@ -0,0 +1,173 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "radio.RadioService.jni"
+#define LOG_NDEBUG 0
+
+#include "com_android_server_radio_RadioService.h"
+
+#include "com_android_server_radio_Tuner.h"
+
+#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
+#include <core_jni_helpers.h>
+#include <utils/Log.h>
+#include <JNIHelp.h>
+
+namespace android {
+
+using hardware::Return;
+using hardware::hidl_vec;
+
+namespace V1_0 = hardware::broadcastradio::V1_0;
+namespace V1_1 = hardware::broadcastradio::V1_1;
+
+using V1_0::Class;
+using V1_0::IBroadcastRadio;
+using V1_0::Result;
+
+using V1_0::BandConfig;
+using V1_0::ProgramInfo;
+using V1_0::MetaData;
+using V1_0::ITuner;
+
+static Mutex gContextMutex;
+
+static jclass gTunerClass;
+static jmethodID gTunerCstor;
+
+static jclass gServiceClass;
+
+struct ServiceContext {
+ ServiceContext() {}
+
+ sp<IBroadcastRadio> mModule;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ServiceContext);
+};
+
+
+/**
+ * Always lock gContextMutex when using native context.
+ */
+static ServiceContext& getNativeContext(jlong nativeContextHandle) {
+ auto nativeContext = reinterpret_cast<ServiceContext*>(nativeContextHandle);
+ LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
+ return *nativeContext;
+}
+
+static jlong nativeInit(JNIEnv *env, jobject obj) {
+ ALOGV("nativeInit()");
+ AutoMutex _l(gContextMutex);
+
+ auto nativeContext = new ServiceContext();
+ static_assert(sizeof(jlong) >= sizeof(nativeContext), "jlong is smaller than a pointer");
+ return reinterpret_cast<jlong>(nativeContext);
+}
+
+static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
+ ALOGV("nativeFinalize()");
+ AutoMutex _l(gContextMutex);
+
+ auto ctx = reinterpret_cast<ServiceContext*>(nativeContext);
+ delete ctx;
+}
+
+static sp<IBroadcastRadio> getModule(jlong nativeContext) {
+ ALOGV("getModule()");
+ AutoMutex _l(gContextMutex);
+ auto& ctx = getNativeContext(nativeContext);
+
+ if (ctx.mModule != nullptr) {
+ return ctx.mModule;
+ }
+
+ // TODO(b/36863239): what about other HAL implementations?
+ auto factory = V1_0::IBroadcastRadioFactory::getService();
+ if (factory == nullptr) {
+ ALOGE("Can't retrieve radio HAL implementation");
+ return nullptr;
+ }
+
+ sp<IBroadcastRadio> module = nullptr;
+ // TODO(b/36863239): not only AM/FM
+ factory->connectModule(Class::AM_FM, [&](Result retval, const sp<IBroadcastRadio>& result) {
+ if (retval == Result::OK) {
+ module = result;
+ }
+ });
+
+ ALOGE_IF(module == nullptr, "Couldn't connect module");
+ ctx.mModule = module;
+ return module;
+}
+
+static jobject openTunerNative(JNIEnv *env, jobject obj, long nativeContext, bool withAudio) {
+ ALOGV("openTunerNative()");
+
+ auto module = getModule(nativeContext);
+ if (module == nullptr) {
+ return nullptr;
+ }
+
+ jobject tuner = env->NewObject(gTunerClass, gTunerCstor);
+ if (tuner == nullptr) {
+ ALOGE("Unable to create new tuner object.");
+ return nullptr;
+ }
+
+ BandConfig bandConfig = {}; // TODO(b/36863239): convert from parameters
+ auto tunerCb = android_server_radio_Tuner_getCallback(env, tuner);
+ Result halResult;
+ sp<ITuner> halTuner = nullptr;
+
+ auto hidlResult = module->openTuner(bandConfig, withAudio, tunerCb,
+ [&](Result result, const sp<ITuner>& tuner) {
+ halResult = result;
+ halTuner = tuner;
+ });
+ if (!hidlResult.isOk() || halResult != Result::OK || halTuner == nullptr) {
+ ALOGE("Couldn't open tuner");
+ ALOGE_IF(hidlResult.isOk(), "halResult = %d", halResult);
+ ALOGE_IF(!hidlResult.isOk(), "hidlResult = %s", hidlResult.description().c_str());
+ env->DeleteLocalRef(tuner);
+ return nullptr;
+ }
+
+ android_server_radio_Tuner_setHalTuner(env, tuner, halTuner);
+ return tuner;
+}
+
+static const JNINativeMethod gRadioServiceMethods[] = {
+ { "nativeInit", "()J", (void*)nativeInit },
+ { "nativeFinalize", "(J)V", (void*)nativeFinalize },
+ { "openTunerNative", "(JZ)Lcom/android/server/radio/Tuner;", (void*)openTunerNative },
+};
+
+void register_android_server_radio_RadioService(JNIEnv *env) {
+ auto tunerClass = FindClassOrDie(env, "com/android/server/radio/Tuner");
+ gTunerClass = MakeGlobalRefOrDie(env, tunerClass);
+ gTunerCstor = GetMethodIDOrDie(env, tunerClass, "<init>", "()V");
+
+ auto serviceClass = FindClassOrDie(env, "com/android/server/radio/RadioService");
+ gServiceClass = MakeGlobalRefOrDie(env, serviceClass);
+
+ auto res = jniRegisterNativeMethods(env, "com/android/server/radio/RadioService",
+ gRadioServiceMethods, NELEM(gRadioServiceMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+}
+
+} /* namespace android */
diff --git a/services/core/jni/com_android_server_radio_RadioService.h b/services/core/jni/com_android_server_radio_RadioService.h
new file mode 100644
index 0000000..1fef9e6
--- /dev/null
+++ b/services/core/jni/com_android_server_radio_RadioService.h
@@ -0,0 +1,28 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_RADIO_RADIOSERVICE_H
+#define _ANDROID_SERVER_RADIO_RADIOSERVICE_H
+
+#include "jni.h"
+
+namespace android {
+
+void register_android_server_radio_RadioService(JNIEnv *env);
+
+} // namespace android
+
+#endif // _ANDROID_SERVER_RADIO_RADIOSERVICE_H
diff --git a/services/core/jni/com_android_server_radio_Tuner.cpp b/services/core/jni/com_android_server_radio_Tuner.cpp
new file mode 100644
index 0000000..f3180a0
--- /dev/null
+++ b/services/core/jni/com_android_server_radio_Tuner.cpp
@@ -0,0 +1,138 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "radio.Tuner.jni"
+#define LOG_NDEBUG 0
+
+#include "com_android_server_radio_Tuner.h"
+
+#include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
+#include <core_jni_helpers.h>
+#include <utils/Log.h>
+#include <JNIHelp.h>
+
+namespace android {
+
+using hardware::Return;
+using hardware::hidl_vec;
+
+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;
+using V1_1::ProgramListResult;
+
+static Mutex gContextMutex;
+
+static jclass gTunerClass;
+static jfieldID gNativeContextId;
+
+struct TunerContext {
+ TunerContext() {}
+
+ sp<ITuner> mHalTuner;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(TunerContext);
+};
+
+// TODO(b/36863239): implement actual callback class which forwards calls to Java code.
+class DummyTunerCallback : public ITunerCallback {
+ virtual Return<void> hardwareFailure() { return Return<void>(); }
+ virtual Return<void> configChange(Result result, const BandConfig& config) {
+ return Return<void>();
+ }
+ virtual Return<void> tuneComplete(Result result, const V1_0::ProgramInfo& info) {
+ return Return<void>();
+ }
+ virtual Return<void> afSwitch(const V1_0::ProgramInfo& info) { return Return<void>(); }
+ virtual Return<void> antennaStateChange(bool connected) { return Return<void>(); }
+ virtual Return<void> trafficAnnouncement(bool active) { return Return<void>(); }
+ virtual Return<void> emergencyAnnouncement(bool active) { return Return<void>(); }
+ virtual Return<void> newMetadata(uint32_t channel, uint32_t subChannel,
+ const hidl_vec<MetaData>& metadata) { return Return<void>(); }
+ virtual Return<void> tuneComplete_1_1(Result result, const V1_1::ProgramInfo& info) {
+ return Return<void>();
+ }
+ virtual Return<void> afSwitch_1_1(const V1_1::ProgramInfo& info) { return Return<void>(); }
+ virtual Return<void> backgroundScanAvailable(bool isAvailable) { return Return<void>(); }
+ virtual Return<void> backgroundScanComplete(ProgramListResult result) { return Return<void>(); }
+ virtual Return<void> programListChanged() { return Return<void>(); }
+};
+
+/**
+ * Always lock gContextMutex when using native context.
+ */
+static TunerContext& getNativeContext(JNIEnv *env, jobject obj) {
+ auto nativeContext = reinterpret_cast<TunerContext*>(env->GetLongField(obj, gNativeContextId));
+ LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
+ return *nativeContext;
+}
+
+static jlong nativeInit(JNIEnv *env, jobject obj) {
+ ALOGV("nativeInit()");
+ AutoMutex _l(gContextMutex);
+
+ auto nativeContext = new TunerContext();
+ static_assert(sizeof(jlong) >= sizeof(nativeContext), "jlong is smaller than a pointer");
+ return reinterpret_cast<jlong>(nativeContext);
+}
+
+static void nativeFinalize(JNIEnv *env, jobject obj, jlong nativeContext) {
+ ALOGV("nativeFinalize()");
+ AutoMutex _l(gContextMutex);
+
+ auto ctx = reinterpret_cast<TunerContext*>(nativeContext);
+ delete ctx;
+}
+
+void android_server_radio_Tuner_setHalTuner(JNIEnv *env, jobject obj, sp<ITuner> halTuner) {
+ ALOGV("setHalTuner(%p)", halTuner.get());
+ AutoMutex _l(gContextMutex);
+
+ auto& ctx = getNativeContext(env, obj);
+ ctx.mHalTuner = halTuner;
+}
+
+sp<ITunerCallback> android_server_radio_Tuner_getCallback(JNIEnv *env, jobject obj) {
+ return new DummyTunerCallback();
+}
+
+static void close(JNIEnv *env, jobject obj) {
+ android_server_radio_Tuner_setHalTuner(env, obj, nullptr);
+}
+
+static const JNINativeMethod gTunerMethods[] = {
+ { "nativeInit", "()J", (void*)nativeInit },
+ { "nativeFinalize", "(J)V", (void*)nativeFinalize },
+ { "close", "()V", (void*)close },
+};
+
+void register_android_server_radio_Tuner(JNIEnv *env) {
+ auto tunerClass = FindClassOrDie(env, "com/android/server/radio/Tuner");
+ gTunerClass = MakeGlobalRefOrDie(env, tunerClass);
+ gNativeContextId = GetFieldIDOrDie(env, gTunerClass, "mNativeContext", "J");
+
+ auto res = jniRegisterNativeMethods(env, "com/android/server/radio/Tuner",
+ gTunerMethods, NELEM(gTunerMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+}
+
+} /* namespace android */
diff --git a/services/core/jni/com_android_server_radio_Tuner.h b/services/core/jni/com_android_server_radio_Tuner.h
new file mode 100644
index 0000000..ec16a18
--- /dev/null
+++ b/services/core/jni/com_android_server_radio_Tuner.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_SERVER_RADIO_TUNER_H
+#define _ANDROID_SERVER_RADIO_TUNER_H
+
+#include "jni.h"
+
+#include <android/hardware/broadcastradio/1.1/ITuner.h>
+#include <android/hardware/broadcastradio/1.1/ITunerCallback.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+void register_android_server_radio_Tuner(JNIEnv *env);
+
+void android_server_radio_Tuner_setHalTuner(JNIEnv *env, jobject obj,
+ sp<hardware::broadcastradio::V1_0::ITuner> halTuner);
+
+sp<hardware::broadcastradio::V1_1::ITunerCallback>
+android_server_radio_Tuner_getCallback(JNIEnv *env, jobject obj);
+
+} // namespace android
+
+#endif // _ANDROID_SERVER_RADIO_TUNER_H
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index f22b330..13ff28c 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -19,6 +19,9 @@
#include "utils/Log.h"
#include "utils/misc.h"
+#include "com_android_server_radio_RadioService.h"
+#include "com_android_server_radio_Tuner.h"
+
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryStatsService(JNIEnv* env);
@@ -62,6 +65,8 @@
}
ALOG_ASSERT(env, "Could not retrieve the env!");
+ register_android_server_radio_RadioService(env);
+ register_android_server_radio_Tuner(env);
register_android_server_PowerManagerService(env);
register_android_server_SerialService(env);
register_android_server_InputApplicationHandle(env);