/* * 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 FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_ #define FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_ #include #include #include #include #include #include #include #include #include #include "core_jni_helpers.h" #include "jni.h" namespace android { template class EventLogHelper { public: static void Init(JNIEnv* env) { struct { const char *name; jclass *clazz; } gClasses[] = { { EventClassDescriptor, &gEventClass }, { "java/lang/Integer", &gIntegerClass }, { "java/lang/Long", &gLongClass }, { "java/lang/Float", &gFloatClass }, { "java/lang/String", &gStringClass }, { "java/util/Collection", &gCollectionClass }, }; struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = { { &gIntegerClass, "value", "I", &gIntegerValueID }, { &gLongClass, "value", "J", &gLongValueID }, { &gFloatClass, "value", "F", &gFloatValueID }, }; struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = { { &gEventClass, "", "([B)V", &gEventInitID }, { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID }, }; for (size_t i = 0; i < NELEM(gClasses); ++i) { ScopedLocalRef clazz(env, FindClassOrDie(env, gClasses[i].name)); *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz.get()); } for (size_t i = 0; i < NELEM(gFields); ++i) { *gFields[i].id = GetFieldIDOrDie(env, *gFields[i].c, gFields[i].name, gFields[i].ft); } for (size_t i = 0; i < NELEM(gMethods); ++i) { *gMethods[i].id = GetMethodIDOrDie(env, *gMethods[i].c, gMethods[i].name, gMethods[i].mt); } } static jint writeEventInteger(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, jint tag, jint value) { android_log_event_list ctx(tag); ctx << (int32_t)value; return ctx.write(LogID); } static jint writeEventLong(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, jint tag, jlong value) { android_log_event_list ctx(tag); ctx << (int64_t)value; return ctx.write(LogID); } static jint writeEventFloat(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, jint tag, jfloat value) { android_log_event_list ctx(tag); ctx << (float)value; return ctx.write(LogID); } static jint writeEventString(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag, jstring value) { android_log_event_list ctx(tag); // Don't throw NPE -- I feel like it's sort of mean for a logging function // to be all crashy if you pass in NULL -- but make the NULL value explicit. ctx << (value != nullptr ? ScopedUtfChars(env, value).c_str() : "NULL"); return ctx.write(LogID); } static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag, jobjectArray value) { android_log_event_list ctx(tag); if (value == nullptr) { ctx << "[NULL]"; return ctx.write(LogID); } jsize copied = 0, num = env->GetArrayLength(value); for (; copied < num && copied < 255; ++copied) { if (ctx.status()) break; ScopedLocalRef item(env, env->GetObjectArrayElement(value, copied)); if (item == nullptr) { ctx << "NULL"; } else if (env->IsInstanceOf(item.get(), gStringClass)) { ctx << ScopedUtfChars(env, (jstring) item.get()).c_str(); } else if (env->IsInstanceOf(item.get(), gIntegerClass)) { ctx << (int32_t)env->GetIntField(item.get(), gIntegerValueID); } else if (env->IsInstanceOf(item.get(), gLongClass)) { ctx << (int64_t)env->GetLongField(item.get(), gLongValueID); } else if (env->IsInstanceOf(item.get(), gFloatClass)) { ctx << (float)env->GetFloatField(item.get(), gFloatValueID); } else { jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid payload item type"); return -1; } } return ctx.write(LogID); } static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) { readEvents(env, loggerMode, nullptr, startTime, out); } static void readEvents(JNIEnv* env, int loggerMode, jintArray jTags, jlong startTime, jobject out) { std::unique_ptr logger_list( nullptr, android_logger_list_close); if (startTime) { logger_list.reset(android_logger_list_alloc_time(loggerMode, log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0)); } else { logger_list.reset(android_logger_list_alloc(loggerMode, 0, 0)); } if (!logger_list) { jniThrowIOException(env, errno); return; } if (!android_logger_open(logger_list.get(), LogID)) { jniThrowIOException(env, errno); return; } ScopedIntArrayRO tags(env); if (jTags != nullptr) { tags.reset(jTags); } while (1) { log_msg log_msg; int ret = android_logger_list_read(logger_list.get(), &log_msg); if (ret == 0) { return; } if (ret < 0) { if (ret == -EINTR) { continue; } if (ret == -EINVAL) { jniThrowException(env, "java/io/IOException", "Event too short"); } else if (ret != -EAGAIN) { jniThrowIOException(env, -ret); // Will throw on return } return; } if (log_msg.id() != LogID) { continue; } int32_t tag = * (int32_t *) log_msg.msg(); if (jTags != nullptr) { bool found = false; for (size_t i = 0; !found && i < tags.size(); ++i) { found = (tag == tags[i]); } if (!found) { continue; } } jsize len = ret; ScopedLocalRef array(env, env->NewByteArray(len)); if (array == nullptr) { return; } { ScopedByteArrayRW bytes(env, array.get()); memcpy(bytes.get(), log_msg.buf, len); } ScopedLocalRef event(env, env->NewObject(gEventClass, gEventInitID, array.get())); if (event == nullptr) { return; } env->CallBooleanMethod(out, gCollectionAddID, event.get()); if (env->ExceptionCheck() == JNI_TRUE) { return; } } } private: static jclass gCollectionClass; static jmethodID gCollectionAddID; static jclass gEventClass; static jmethodID gEventInitID; static jclass gIntegerClass; static jfieldID gIntegerValueID; static jclass gLongClass; static jfieldID gLongValueID; static jclass gFloatClass; static jfieldID gFloatValueID; static jclass gStringClass; }; // Explicit instantiation declarations. template jclass EventLogHelper::gCollectionClass; template jmethodID EventLogHelper::gCollectionAddID; template jclass EventLogHelper::gEventClass; template jmethodID EventLogHelper::gEventInitID; template jclass EventLogHelper::gIntegerClass; template jfieldID EventLogHelper::gIntegerValueID; template jclass EventLogHelper::gLongClass; template jfieldID EventLogHelper::gLongValueID; template jclass EventLogHelper::gFloatClass; template jfieldID EventLogHelper::gFloatValueID; template jclass EventLogHelper::gStringClass; } // namespace android #endif // FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_