From: Andreas Gampe Date: Mon, 9 Oct 2017 23:21:11 +0000 (-0700) Subject: {Event,Security}Log: Unify code X-Git-Tag: android-x86-9.0-r1~348^2~1^2~3^2~6^2~45^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=2e220ef36c680a3615502dfa1051515dd4f095ac;p=android-x86%2Fframeworks-base.git {Event,Security}Log: Unify code Extract common code into eventlog_helper. Bug: 67511924 Test: m Test: Device boots Test: cts-tradefed run commandAndExit cts -m CtsUtilTestCases -t android.util.cts.EventLogTest Change-Id: Idafad832385f57c22f40dbb8570e95da6d44f08f --- diff --git a/core/jni/android_app_admin_SecurityLog.cpp b/core/jni/android_app_admin_SecurityLog.cpp index be3eef75fe71..b3bcaa0f7f03 100644 --- a/core/jni/android_app_admin_SecurityLog.cpp +++ b/core/jni/android_app_admin_SecurityLog.cpp @@ -14,161 +14,26 @@ * limitations under the License. */ -#include - -#include #include #include #include -#include "core_jni_helpers.h" #include "jni.h" -namespace android { - -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; +#include "core_jni_helpers.h" +#include "eventlog_helper.h" -static jclass gStringClass; +namespace android { +constexpr char kSecurityLogEventClass[] = "android/app/admin/SecurityLog$SecurityEvent"; +template class EventLogHelper; +using SLog = EventLogHelper; static jboolean android_app_admin_SecurityLog_isLoggingEnabled(JNIEnv* env, jobject /* clazz */) { return (bool)__android_log_security(); } -static jint android_app_admin_SecurityLog_writeEvent_String(JNIEnv* env, - jobject /* clazz */, - 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. - if (value != NULL) { - const char *str = env->GetStringUTFChars(value, NULL); - ctx << str; - env->ReleaseStringUTFChars(value, str); - } else { - ctx << "NULL"; - } - return ctx.write(log_id_t::LOG_ID_SECURITY); -} - -static jint android_app_admin_SecurityLog_writeEvent_Array(JNIEnv* env, jobject clazz, - jint tag, jobjectArray value) { - android_log_event_list ctx(tag); - - if (value == NULL) { - ctx << "[NULL]"; - return ctx.write(log_id_t::LOG_ID_SECURITY); - } - - jsize copied = 0, num = env->GetArrayLength(value); - for (; copied < num && copied < 255; ++copied) { - if (ctx.status()) break; - jobject item = env->GetObjectArrayElement(value, copied); - if (item == NULL) { - ctx << "NULL"; - } else if (env->IsInstanceOf(item, gStringClass)) { - const char *str = env->GetStringUTFChars((jstring) item, NULL); - ctx << str; - env->ReleaseStringUTFChars((jstring) item, str); - } else if (env->IsInstanceOf(item, gIntegerClass)) { - ctx << (int32_t)env->GetIntField(item, gIntegerValueID); - } else if (env->IsInstanceOf(item, gLongClass)) { - ctx << (int64_t)env->GetLongField(item, gLongValueID); - } else if (env->IsInstanceOf(item, gFloatClass)) { - ctx << (float)env->GetFloatField(item, gFloatValueID); - } else { - jniThrowException(env, - "java/lang/IllegalArgumentException", - "Invalid payload item type"); - return -1; - } - env->DeleteLocalRef(item); - } - return ctx.write(log_id_t::LOG_ID_SECURITY); -} - -static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) { - struct logger_list *logger_list; - if (startTime) { - logger_list = android_logger_list_alloc_time(loggerMode, - log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0); - } else { - logger_list = android_logger_list_alloc(loggerMode, 0, 0); - } - if (!logger_list) { - jniThrowIOException(env, errno); - return; - } - - if (!android_logger_open(logger_list, LOG_ID_SECURITY)) { - jniThrowIOException(env, errno); - android_logger_list_free(logger_list); - return; - } - - while (1) { - log_msg log_msg; - int ret = android_logger_list_read(logger_list, &log_msg); - - if (ret == 0) { - break; - } - 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 - } - break; - } - - if (log_msg.id() != LOG_ID_SECURITY) { - continue; - } - - jsize len = ret; - jbyteArray array = env->NewByteArray(len); - if (array == NULL) { - break; - } - - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, log_msg.buf, len); - env->ReleaseByteArrayElements(array, bytes, 0); - - jobject event = env->NewObject(gEventClass, gEventInitID, array); - if (event == NULL) { - break; - } - - env->CallBooleanMethod(out, gCollectionAddID, event); - env->DeleteLocalRef(event); - env->DeleteLocalRef(array); - if (env->ExceptionCheck() == JNI_TRUE) { - break; - } - } - - android_logger_list_close(logger_list); -} - static void android_app_admin_SecurityLog_readEvents(JNIEnv* env, jobject /* clazz */, jobject out) { @@ -176,7 +41,7 @@ static void android_app_admin_SecurityLog_readEvents(JNIEnv* env, jobject /* cla jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out); + SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out); } static void android_app_admin_SecurityLog_readEventsSince(JNIEnv* env, jobject /* clazz */, @@ -187,7 +52,7 @@ static void android_app_admin_SecurityLog_readEventsSince(JNIEnv* env, jobject / jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out); + SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out); } static void android_app_admin_SecurityLog_readPreviousEvents(JNIEnv* env, jobject /* clazz */, @@ -197,7 +62,7 @@ static void android_app_admin_SecurityLog_readPreviousEvents(JNIEnv* env, jobjec jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out); + SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out); } static void android_app_admin_SecurityLog_readEventsOnWrapping(JNIEnv* env, jobject /* clazz */, @@ -207,7 +72,8 @@ static void android_app_admin_SecurityLog_readEventsOnWrapping(JNIEnv* env, jobj jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp, out); + SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp, + out); } /* @@ -221,11 +87,11 @@ static const JNINativeMethod gRegisterMethods[] = { }, { "writeEvent", "(ILjava/lang/String;)I", - (void*) android_app_admin_SecurityLog_writeEvent_String + (void*) SLog::writeEventString }, { "writeEvent", "(I[Ljava/lang/Object;)I", - (void*) android_app_admin_SecurityLog_writeEvent_Array + (void*) SLog::writeEventArray }, { "readEvents", "(Ljava/util/Collection;)V", @@ -245,41 +111,8 @@ static const JNINativeMethod gRegisterMethods[] = { }, }; -static struct { const char *name; jclass *clazz; } gClasses[] = { - { "android/app/admin/SecurityLog$SecurityEvent", &gEventClass }, - { "java/lang/Integer", &gIntegerClass }, - { "java/lang/Long", &gLongClass }, - { "java/lang/Float", &gFloatClass }, - { "java/lang/String", &gStringClass }, - { "java/util/Collection", &gCollectionClass }, -}; - -static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = { - { &gIntegerClass, "value", "I", &gIntegerValueID }, - { &gLongClass, "value", "J", &gLongValueID }, - { &gFloatClass, "value", "F", &gFloatValueID }, -}; - -static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = { - { &gEventClass, "", "([B)V", &gEventInitID }, - { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID }, -}; - int register_android_app_admin_SecurityLog(JNIEnv* env) { - for (int i = 0; i < NELEM(gClasses); ++i) { - jclass clazz = FindClassOrDie(env, gClasses[i].name); - *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz); - } - - for (int i = 0; i < NELEM(gFields); ++i) { - *gFields[i].id = GetFieldIDOrDie(env, - *gFields[i].c, gFields[i].name, gFields[i].ft); - } - - for (int i = 0; i < NELEM(gMethods); ++i) { - *gMethods[i].id = GetMethodIDOrDie(env, - *gMethods[i].c, gMethods[i].name, gMethods[i].mt); - } + SLog::Init(env); return RegisterMethodsOrDie( env, diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp index 76ce30779ced..3b5a144a4e61 100644 --- a/core/jni/android_util_EventLog.cpp +++ b/core/jni/android_util_EventLog.cpp @@ -14,217 +14,20 @@ * limitations under the License. */ -#include - -#include - -#include +#include +#include #include -#include "core_jni_helpers.h" #include "jni.h" -#define UNUSED __attribute__((__unused__)) +#include "core_jni_helpers.h" +#include "eventlog_helper.h" namespace android { -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; - -/* - * In class android.util.EventLog: - * static native int writeEvent(int tag, int value) - */ -static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env UNUSED, - jobject clazz UNUSED, - jint tag, jint value) -{ - android_log_event_list ctx(tag); - ctx << (int32_t)value; - return ctx.write(); -} - -/* - * In class android.util.EventLog: - * static native int writeEvent(long tag, long value) - */ -static jint android_util_EventLog_writeEvent_Long(JNIEnv* env UNUSED, - jobject clazz UNUSED, - jint tag, jlong value) -{ - android_log_event_list ctx(tag); - ctx << (int64_t)value; - return ctx.write(); -} - -/* - * In class android.util.EventLog: - * static native int writeEvent(long tag, float value) - */ -static jint android_util_EventLog_writeEvent_Float(JNIEnv* env UNUSED, - jobject clazz UNUSED, - jint tag, jfloat value) -{ - android_log_event_list ctx(tag); - ctx << (float)value; - return ctx.write(); -} - -/* - * In class android.util.EventLog: - * static native int writeEvent(int tag, String value) - */ -static jint android_util_EventLog_writeEvent_String(JNIEnv* env, - jobject clazz 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. - if (value != NULL) { - const char *str = env->GetStringUTFChars(value, NULL); - ctx << str; - env->ReleaseStringUTFChars(value, str); - } else { - ctx << "NULL"; - } - return ctx.write(); -} - -/* - * In class android.util.EventLog: - * static native int writeEvent(long tag, Object... value) - */ -static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz, - jint tag, jobjectArray value) { - android_log_event_list ctx(tag); - - if (value == NULL) { - ctx << "[NULL]"; - return ctx.write(); - } - - jsize copied = 0, num = env->GetArrayLength(value); - for (; copied < num && copied < 255; ++copied) { - if (ctx.status()) break; - jobject item = env->GetObjectArrayElement(value, copied); - if (item == NULL) { - ctx << "NULL"; - } else if (env->IsInstanceOf(item, gStringClass)) { - const char *str = env->GetStringUTFChars((jstring) item, NULL); - ctx << str; - env->ReleaseStringUTFChars((jstring) item, str); - } else if (env->IsInstanceOf(item, gIntegerClass)) { - ctx << (int32_t)env->GetIntField(item, gIntegerValueID); - } else if (env->IsInstanceOf(item, gLongClass)) { - ctx << (int64_t)env->GetLongField(item, gLongValueID); - } else if (env->IsInstanceOf(item, gFloatClass)) { - ctx << (float)env->GetFloatField(item, gFloatValueID); - } else { - jniThrowException(env, - "java/lang/IllegalArgumentException", - "Invalid payload item type"); - return -1; - } - env->DeleteLocalRef(item); - } - return ctx.write(); -} - -static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime, jobject out) { - struct logger_list *logger_list; - if (startTime) { - logger_list = android_logger_list_alloc_time(loggerMode, - log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0); - } else { - logger_list = android_logger_list_alloc(loggerMode, 0, 0); - } - if (!logger_list) { - jniThrowIOException(env, errno); - return; - } - - if (!android_logger_open(logger_list, LOG_ID_EVENTS)) { - jniThrowIOException(env, errno); - android_logger_list_free(logger_list); - return; - } - - jsize tagLength = env->GetArrayLength(tags); - jint *tagValues = env->GetIntArrayElements(tags, NULL); - - while (1) { - log_msg log_msg; - int ret = android_logger_list_read(logger_list, &log_msg); - - if (ret == 0) { - break; - } - 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 - } - break; - } - - if (log_msg.id() != LOG_ID_EVENTS) { - continue; - } - - int32_t tag = * (int32_t *) log_msg.msg(); - - int found = 0; - for (int i = 0; !found && i < tagLength; ++i) { - found = (tag == tagValues[i]); - } - - if (found) { - jsize len = ret; - jbyteArray array = env->NewByteArray(len); - if (array == NULL) { - break; - } - - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, log_msg.buf, len); - env->ReleaseByteArrayElements(array, bytes, 0); - - jobject event = env->NewObject(gEventClass, gEventInitID, array); - if (event == NULL) { - break; - } - - env->CallBooleanMethod(out, gCollectionAddID, event); - env->DeleteLocalRef(event); - env->DeleteLocalRef(array); - if (env->ExceptionCheck() == JNI_TRUE) { - break; - } - } - } - - android_logger_list_close(logger_list); - - env->ReleaseIntArrayElements(tags, tagValues, 0); -} +constexpr char kEventLogEventClass[] = "android/util/EventLog$Event"; +template class EventLogHelper; +using ELog = EventLogHelper; /* * In class android.util.EventLog: @@ -232,7 +35,7 @@ static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startT * * Reads events from the event log */ -static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED, +static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jintArray tags, jobject out) { @@ -241,7 +44,7 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED, return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out); + ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out); } /* * In class android.util.EventLog: @@ -249,7 +52,7 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED, * * Reads events from the event log, blocking until events after timestamp are to be overwritten. */ -static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz UNUSED, +static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jintArray tags, jlong timestamp, jobject out) { @@ -257,8 +60,8 @@ static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject claz jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, - tags, timestamp, out); + ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, tags, + timestamp, out); } /* @@ -266,17 +69,11 @@ static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject claz */ static const JNINativeMethod gRegisterMethods[] = { /* name, signature, funcPtr */ - { "writeEvent", "(II)I", (void*) android_util_EventLog_writeEvent_Integer }, - { "writeEvent", "(IJ)I", (void*) android_util_EventLog_writeEvent_Long }, - { "writeEvent", "(IF)I", (void*) android_util_EventLog_writeEvent_Float }, - { "writeEvent", - "(ILjava/lang/String;)I", - (void*) android_util_EventLog_writeEvent_String - }, - { "writeEvent", - "(I[Ljava/lang/Object;)I", - (void*) android_util_EventLog_writeEvent_Array - }, + { "writeEvent", "(II)I", (void*) ELog::writeEventInteger }, + { "writeEvent", "(IJ)I", (void*) ELog::writeEventLong }, + { "writeEvent", "(IF)I", (void*) ELog::writeEventFloat }, + { "writeEvent", "(ILjava/lang/String;)I", (void*) ELog::writeEventString }, + { "writeEvent", "(I[Ljava/lang/Object;)I", (void*) ELog::writeEventArray }, { "readEvents", "([ILjava/util/Collection;)V", (void*) android_util_EventLog_readEvents @@ -287,41 +84,8 @@ static const JNINativeMethod gRegisterMethods[] = { }, }; -static struct { const char *name; jclass *clazz; } gClasses[] = { - { "android/util/EventLog$Event", &gEventClass }, - { "java/lang/Integer", &gIntegerClass }, - { "java/lang/Long", &gLongClass }, - { "java/lang/Float", &gFloatClass }, - { "java/lang/String", &gStringClass }, - { "java/util/Collection", &gCollectionClass }, -}; - -static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = { - { &gIntegerClass, "value", "I", &gIntegerValueID }, - { &gLongClass, "value", "J", &gLongValueID }, - { &gFloatClass, "value", "F", &gFloatValueID }, -}; - -static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = { - { &gEventClass, "", "([B)V", &gEventInitID }, - { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID }, -}; - int register_android_util_EventLog(JNIEnv* env) { - for (int i = 0; i < NELEM(gClasses); ++i) { - jclass clazz = FindClassOrDie(env, gClasses[i].name); - *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz); - } - - for (int i = 0; i < NELEM(gFields); ++i) { - *gFields[i].id = GetFieldIDOrDie(env, - *gFields[i].c, gFields[i].name, gFields[i].ft); - } - - for (int i = 0; i < NELEM(gMethods); ++i) { - *gMethods[i].id = GetMethodIDOrDie(env, - *gMethods[i].c, gMethods[i].name, gMethods[i].mt); - } + ELog::Init(env); return RegisterMethodsOrDie( env, diff --git a/core/jni/eventlog_helper.h b/core/jni/eventlog_helper.h new file mode 100644 index 000000000000..1101b837b589 --- /dev/null +++ b/core/jni/eventlog_helper.h @@ -0,0 +1,285 @@ +/* + * 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 "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. + if (value != NULL) { + const char *str = env->GetStringUTFChars(value, NULL); + ctx << str; + env->ReleaseStringUTFChars(value, str); + } else { + ctx << "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 == NULL) { + ctx << "[NULL]"; + return ctx.write(LogID); + } + + jsize copied = 0, num = env->GetArrayLength(value); + for (; copied < num && copied < 255; ++copied) { + if (ctx.status()) break; + jobject item = env->GetObjectArrayElement(value, copied); + if (item == NULL) { + ctx << "NULL"; + } else if (env->IsInstanceOf(item, gStringClass)) { + const char *str = env->GetStringUTFChars((jstring) item, NULL); + ctx << str; + env->ReleaseStringUTFChars((jstring) item, str); + } else if (env->IsInstanceOf(item, gIntegerClass)) { + ctx << (int32_t)env->GetIntField(item, gIntegerValueID); + } else if (env->IsInstanceOf(item, gLongClass)) { + ctx << (int64_t)env->GetLongField(item, gLongValueID); + } else if (env->IsInstanceOf(item, gFloatClass)) { + ctx << (float)env->GetFloatField(item, gFloatValueID); + } else { + jniThrowException(env, + "java/lang/IllegalArgumentException", + "Invalid payload item type"); + return -1; + } + env->DeleteLocalRef(item); + } + 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 tags, jlong startTime, + jobject out) { + struct logger_list *logger_list; + if (startTime) { + logger_list = android_logger_list_alloc_time(loggerMode, + log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0); + } else { + logger_list = android_logger_list_alloc(loggerMode, 0, 0); + } + if (!logger_list) { + jniThrowIOException(env, errno); + return; + } + + if (!android_logger_open(logger_list, LogID)) { + jniThrowIOException(env, errno); + android_logger_list_free(logger_list); + return; + } + + jsize tagLength = 0; + jint *tagValues = nullptr; + if (tags != nullptr) { + tagLength = env->GetArrayLength(tags); + tagValues = env->GetIntArrayElements(tags, NULL); + } + + while (1) { + log_msg log_msg; + int ret = android_logger_list_read(logger_list, &log_msg); + + if (ret == 0) { + break; + } + 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 + } + break; + } + + if (log_msg.id() != LogID) { + continue; + } + + int32_t tag = * (int32_t *) log_msg.msg(); + + if (tags != nullptr) { + bool found = false; + for (int i = 0; !found && i < tagLength; ++i) { + found = (tag == tagValues[i]); + } + if (!found) { + continue; + } + } + + jsize len = ret; + jbyteArray array = env->NewByteArray(len); + if (array == NULL) { + break; + } + + jbyte *bytes = env->GetByteArrayElements(array, NULL); + memcpy(bytes, log_msg.buf, len); + env->ReleaseByteArrayElements(array, bytes, 0); + + jobject event = env->NewObject(gEventClass, gEventInitID, array); + if (event == NULL) { + break; + } + + env->CallBooleanMethod(out, gCollectionAddID, event); + env->DeleteLocalRef(event); + env->DeleteLocalRef(array); + if (env->ExceptionCheck() == JNI_TRUE) { + break; + } + } + + android_logger_list_close(logger_list); + + if (tags != nullptr) { + env->ReleaseIntArrayElements(tags, tagValues, 0); + } + } + +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_