diff options
author | Max Dashouk <mdashouk@google.com> | 2021-05-20 14:19:32 -0700 |
---|---|---|
committer | Max Dashouk <mdashouk@google.com> | 2021-06-02 10:06:30 -0700 |
commit | e864dfcc54b6c5123d7f0afde7358edc394090c9 (patch) | |
tree | 2db6b31ac766ebe3e7cb04f9187c3f336bf93dd0 /cpp/telemetry | |
parent | 7983ec3cc36ee6bfc765e1e82a2a28eef6c987fe (diff) | |
download | Car-e864dfcc54b6c5123d7f0afde7358edc394090c9.tar.gz |
Add utility function that takes Java Bundle, converts it and pushes it
to Lua table.
Adds relevant unit tests.
Bug: 187517413
Test: atest JniUtilsTest
Change-Id: I44a3f83efc5733c83506aad8897161e6f862f996
Diffstat (limited to 'cpp/telemetry')
-rw-r--r-- | cpp/telemetry/script_executor/Android.bp | 48 | ||||
-rw-r--r-- | cpp/telemetry/script_executor/src/JniUtils.cpp | 95 | ||||
-rw-r--r-- | cpp/telemetry/script_executor/src/JniUtils.h | 39 | ||||
-rw-r--r-- | cpp/telemetry/script_executor/src/LuaEngine.cpp | 7 | ||||
-rw-r--r-- | cpp/telemetry/script_executor/src/LuaEngine.h | 7 | ||||
-rw-r--r-- | cpp/telemetry/script_executor/src/tests/JniUtilsTestHelper.cpp | 138 |
6 files changed, 322 insertions, 12 deletions
diff --git a/cpp/telemetry/script_executor/Android.bp b/cpp/telemetry/script_executor/Android.bp index 734d4ddb70..78b639a44a 100644 --- a/cpp/telemetry/script_executor/Android.bp +++ b/cpp/telemetry/script_executor/Android.bp @@ -16,13 +16,47 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +cc_defaults { + name: "scriptexecutor_defaults", + cflags: [ + "-Wno-unused-parameter", + ], + static_libs: [ + "libbase", + "liblog", + "liblua", + ], +} + cc_library { - name: "script_executor", - srcs: [ - "src/LuaEngine.cpp", - ], - static_libs: [ - "liblua", - ], + name: "libscriptexecutor", + defaults: [ + "scriptexecutor_defaults", + ], + srcs: [ + "src/JniUtils.cpp", + "src/LuaEngine.cpp", + "src/ScriptExecutorListener.cpp", + ], + shared_libs: [ + "libnativehelper", + ], + // Allow dependents to use the header files. + export_include_dirs: [ + "src", + ], } +cc_library_shared { + name: "libscriptexecutorjniutils-test", + defaults: [ + "scriptexecutor_defaults", + ], + srcs: [ + "src/tests/JniUtilsTestHelper.cpp", + ], + shared_libs: [ + "libnativehelper", + "libscriptexecutor", + ], +} diff --git a/cpp/telemetry/script_executor/src/JniUtils.cpp b/cpp/telemetry/script_executor/src/JniUtils.cpp new file mode 100644 index 0000000000..93c1af89e6 --- /dev/null +++ b/cpp/telemetry/script_executor/src/JniUtils.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2021, 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. + */ + +#include "JniUtils.h" + +namespace android { +namespace automotive { +namespace telemetry { +namespace script_executor { + +void PushBundleToLuaTable(JNIEnv* env, LuaEngine* luaEngine, jobject bundle) { + lua_newtable(luaEngine->GetLuaState()); + // null bundle object is allowed. We will treat it as an empty table. + if (bundle == nullptr) { + return; + } + + // TODO(b/188832769): Consider caching some of these JNI references for + // performance reasons. + jclass bundleClass = env->FindClass("android/os/Bundle"); + jmethodID getKeySetMethod = env->GetMethodID(bundleClass, "keySet", "()Ljava/util/Set;"); + jobject keys = env->CallObjectMethod(bundle, getKeySetMethod); + jclass setClass = env->FindClass("java/util/Set"); + jmethodID iteratorMethod = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); + jobject keySetIteratorObject = env->CallObjectMethod(keys, iteratorMethod); + + jclass iteratorClass = env->FindClass("java/util/Iterator"); + jmethodID hasNextMethod = env->GetMethodID(iteratorClass, "hasNext", "()Z"); + jmethodID nextMethod = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); + + jclass booleanClass = env->FindClass("java/lang/Boolean"); + jclass integerClass = env->FindClass("java/lang/Integer"); + jclass numberClass = env->FindClass("java/lang/Number"); + jclass stringClass = env->FindClass("java/lang/String"); + // TODO(b/188816922): Handle more types such as float and integer arrays, + // and perhaps nested Bundles. + + jmethodID getMethod = + env->GetMethodID(bundleClass, "get", "(Ljava/lang/String;)Ljava/lang/Object;"); + + // Iterate over key set of the bundle one key at a time. + while (env->CallBooleanMethod(keySetIteratorObject, hasNextMethod)) { + // Read the value object that corresponds to this key. + jstring key = (jstring)env->CallObjectMethod(keySetIteratorObject, nextMethod); + jobject value = env->CallObjectMethod(bundle, getMethod, key); + + // Get the value of the type, extract it accordingly from the bundle and + // push the extracted value and the key to the Lua table. + if (env->IsInstanceOf(value, booleanClass)) { + jmethodID boolMethod = env->GetMethodID(booleanClass, "booleanValue", "()Z"); + bool boolValue = static_cast<bool>(env->CallBooleanMethod(value, boolMethod)); + lua_pushboolean(luaEngine->GetLuaState(), boolValue); + } else if (env->IsInstanceOf(value, integerClass)) { + jmethodID intMethod = env->GetMethodID(integerClass, "intValue", "()I"); + lua_pushinteger(luaEngine->GetLuaState(), env->CallIntMethod(value, intMethod)); + } else if (env->IsInstanceOf(value, numberClass)) { + // Condense other numeric types using one class. Because lua supports only + // integer or double, and we handled integer in previous if clause. + jmethodID numberMethod = env->GetMethodID(numberClass, "doubleValue", "()D"); + /* Pushes a double onto the stack */ + lua_pushnumber(luaEngine->GetLuaState(), env->CallDoubleMethod(value, numberMethod)); + } else if (env->IsInstanceOf(value, stringClass)) { + const char* rawStringValue = env->GetStringUTFChars((jstring)value, nullptr); + lua_pushstring(luaEngine->GetLuaState(), rawStringValue); + env->ReleaseStringUTFChars((jstring)value, rawStringValue); + } else { + // Other types are not implemented yet, skipping. + continue; + } + + const char* rawKey = env->GetStringUTFChars(key, nullptr); + // table[rawKey] = value, where value is on top of the stack, + // and the table is the next element in the stack. + lua_setfield(luaEngine->GetLuaState(), /* idx= */ -2, rawKey); + env->ReleaseStringUTFChars(key, rawKey); + } +} + +} // namespace script_executor +} // namespace telemetry +} // namespace automotive +} // namespace android diff --git a/cpp/telemetry/script_executor/src/JniUtils.h b/cpp/telemetry/script_executor/src/JniUtils.h new file mode 100644 index 0000000000..c3ef6778e4 --- /dev/null +++ b/cpp/telemetry/script_executor/src/JniUtils.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, 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 CPP_TELEMETRY_SCRIPT_EXECUTOR_SRC_JNIUTILS_H_ +#define CPP_TELEMETRY_SCRIPT_EXECUTOR_SRC_JNIUTILS_H_ + +#include "LuaEngine.h" +#include "jni.h" + +namespace android { +namespace automotive { +namespace telemetry { +namespace script_executor { + +// Helper function which takes android.os.Bundle object in "bundle" argument +// and converts it to Lua table on top of Lua stack. All key-value pairs are +// converted to the corresponding key-value pairs of the Lua table as long as +// the Bundle value types are supported. At this point, we support boolean, +// integer, double and String types in Java. +void PushBundleToLuaTable(JNIEnv* env, LuaEngine* luaEngine, jobject bundle); + +} // namespace script_executor +} // namespace telemetry +} // namespace automotive +} // namespace android + +#endif // CPP_TELEMETRY_SCRIPT_EXECUTOR_SRC_JNIUTILS_H_ diff --git a/cpp/telemetry/script_executor/src/LuaEngine.cpp b/cpp/telemetry/script_executor/src/LuaEngine.cpp index a8cace3c1c..cc1d0b8f62 100644 --- a/cpp/telemetry/script_executor/src/LuaEngine.cpp +++ b/cpp/telemetry/script_executor/src/LuaEngine.cpp @@ -28,8 +28,7 @@ namespace automotive { namespace telemetry { namespace script_executor { -LuaEngine::LuaEngine(std::unique_ptr<ScriptExecutorListener> listener) : - mListener(std::move(listener)) { +LuaEngine::LuaEngine() { mLuaState = luaL_newstate(); luaL_openlibs(mLuaState); } @@ -38,6 +37,10 @@ LuaEngine::~LuaEngine() { lua_close(mLuaState); } +lua_State* LuaEngine::GetLuaState() { + return mLuaState; +} + } // namespace script_executor } // namespace telemetry } // namespace automotive diff --git a/cpp/telemetry/script_executor/src/LuaEngine.h b/cpp/telemetry/script_executor/src/LuaEngine.h index 086dbfe19b..a0f3978079 100644 --- a/cpp/telemetry/script_executor/src/LuaEngine.h +++ b/cpp/telemetry/script_executor/src/LuaEngine.h @@ -33,14 +33,15 @@ namespace script_executor { // Encapsulates Lua script execution environment. class LuaEngine { public: - explicit LuaEngine(std::unique_ptr<ScriptExecutorListener> listener); + LuaEngine(); virtual ~LuaEngine(); + // Returns pointer to Lua state object. + lua_State* GetLuaState(); + private: lua_State* mLuaState; // owned - - std::unique_ptr<ScriptExecutorListener> mListener; }; } // namespace script_executor diff --git a/cpp/telemetry/script_executor/src/tests/JniUtilsTestHelper.cpp b/cpp/telemetry/script_executor/src/tests/JniUtilsTestHelper.cpp new file mode 100644 index 0000000000..9e2c43afd2 --- /dev/null +++ b/cpp/telemetry/script_executor/src/tests/JniUtilsTestHelper.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021, 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. + */ + +#include "JniUtils.h" +#include "LuaEngine.h" +#include "jni.h" + +#include <cstdint> +#include <cstring> + +namespace android { +namespace automotive { +namespace telemetry { +namespace script_executor { +namespace { + +extern "C" { + +#include "lua.h" + +JNIEXPORT jlong JNICALL +Java_com_android_car_telemetry_JniUtilsTest_nativeCreateLuaEngine(JNIEnv* env, jobject object) { + // Cast first to intptr_t to ensure int can hold the pointer without loss. + return static_cast<jlong>(reinterpret_cast<intptr_t>(new LuaEngine())); +} + +JNIEXPORT void JNICALL Java_com_android_car_telemetry_JniUtilsTest_nativeDestroyLuaEngine( + JNIEnv* env, jobject object, jlong luaEnginePtr) { + delete reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr)); +} + +JNIEXPORT void JNICALL Java_com_android_car_telemetry_JniUtilsTest_nativePushBundleToLuaTableCaller( + JNIEnv* env, jobject object, jlong luaEnginePtr, jobject bundle) { + PushBundleToLuaTable(env, reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr)), + bundle); +} + +JNIEXPORT jint JNICALL Java_com_android_car_telemetry_JniUtilsTest_nativeGetObjectSize( + JNIEnv* env, jobject object, jlong luaEnginePtr, jint index) { + LuaEngine* engine = reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr)); + return lua_rawlen(engine->GetLuaState(), static_cast<int>(index)); +} + +JNIEXPORT bool JNICALL Java_com_android_car_telemetry_JniUtilsTest_nativeHasBooleanValue( + JNIEnv* env, jobject object, jlong luaEnginePtr, jstring key, jboolean value) { + const char* rawKey = env->GetStringUTFChars(key, nullptr); + LuaEngine* engine = reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr)); + auto* luaState = engine->GetLuaState(); + lua_pushstring(luaState, rawKey); + env->ReleaseStringUTFChars(key, rawKey); + lua_gettable(luaState, -2); + bool result = false; + if (!lua_isboolean(luaState, -1)) + result = false; + else + result = static_cast<bool>(lua_toboolean(luaState, -1)) == static_cast<bool>(value); + lua_pop(luaState, 1); + return result; +} + +JNIEXPORT bool JNICALL Java_com_android_car_telemetry_JniUtilsTest_nativeHasIntValue( + JNIEnv* env, jobject object, jlong luaEnginePtr, jstring key, jint value) { + const char* rawKey = env->GetStringUTFChars(key, nullptr); + LuaEngine* engine = reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr)); + // Assumes the table is on top of the stack. + auto* luaState = engine->GetLuaState(); + lua_pushstring(luaState, rawKey); + env->ReleaseStringUTFChars(key, rawKey); + lua_gettable(luaState, -2); + bool result = false; + if (!lua_isinteger(luaState, -1)) + result = false; + else + result = lua_tointeger(luaState, -1) == static_cast<int>(value); + lua_pop(luaState, 1); + return result; +} + +JNIEXPORT bool JNICALL Java_com_android_car_telemetry_JniUtilsTest_nativeHasDoubleValue( + JNIEnv* env, jobject object, jlong luaEnginePtr, jstring key, jdouble value) { + const char* rawKey = env->GetStringUTFChars(key, nullptr); + LuaEngine* engine = reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr)); + // Assumes the table is on top of the stack. + auto* luaState = engine->GetLuaState(); + lua_pushstring(luaState, rawKey); + env->ReleaseStringUTFChars(key, rawKey); + lua_gettable(luaState, -2); + bool result = false; + if (!lua_isnumber(luaState, -1)) + result = false; + else + result = static_cast<double>(lua_tonumber(luaState, -1)) == static_cast<double>(value); + lua_pop(luaState, 1); + return result; +} + +JNIEXPORT bool JNICALL Java_com_android_car_telemetry_JniUtilsTest_nativeHasStringValue( + JNIEnv* env, jobject object, jlong luaEnginePtr, jstring key, jstring value) { + const char* rawKey = env->GetStringUTFChars(key, nullptr); + LuaEngine* engine = reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr)); + // Assumes the table is on top of the stack. + auto* luaState = engine->GetLuaState(); + lua_pushstring(luaState, rawKey); + env->ReleaseStringUTFChars(key, rawKey); + lua_gettable(luaState, -2); + bool result = false; + if (!lua_isstring(luaState, -1)) { + result = false; + } else { + std::string s = lua_tostring(luaState, -1); + const char* rawValue = env->GetStringUTFChars(value, nullptr); + result = strcmp(lua_tostring(luaState, -1), rawValue) == 0; + env->ReleaseStringUTFChars(value, rawValue); + } + lua_pop(luaState, 1); + return result; +} + +} // extern "C" + +} // namespace +} // namespace script_executor +} // namespace telemetry +} // namespace automotive +} // namespace android |