1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
18#define FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
19
20#include <memory>
21
22#include <fcntl.h>
23
24#include <android-base/macros.h>
25#include <log/log_event_list.h>
26
27#include <log/log.h>
28
29#include <nativehelper/JNIHelp.h>
30#include <nativehelper/ScopedLocalRef.h>
31#include <nativehelper/ScopedPrimitiveArray.h>
32#include <nativehelper/ScopedUtfChars.h>
33#include "core_jni_helpers.h"
34#include "jni.h"
35
36namespace android {
37
38template <log_id_t LogID, const char* EventClassDescriptor>
39class EventLogHelper {
40public:
41    static void Init(JNIEnv* env) {
42        struct { const char *name; jclass *clazz; } gClasses[] = {
43                { EventClassDescriptor, &gEventClass },
44                { "java/lang/Integer", &gIntegerClass },
45                { "java/lang/Long", &gLongClass },
46                { "java/lang/Float", &gFloatClass },
47                { "java/lang/String", &gStringClass },
48                { "java/util/Collection", &gCollectionClass },
49        };
50        struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
51                { &gIntegerClass, "value", "I", &gIntegerValueID },
52                { &gLongClass, "value", "J", &gLongValueID },
53                { &gFloatClass, "value", "F", &gFloatValueID },
54        };
55        struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
56                { &gEventClass, "<init>", "([B)V", &gEventInitID },
57                { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
58        };
59
60        for (size_t i = 0; i < NELEM(gClasses); ++i) {
61            ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, gClasses[i].name));
62            *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz.get());
63        }
64        for (size_t i = 0; i < NELEM(gFields); ++i) {
65            *gFields[i].id = GetFieldIDOrDie(env,
66                    *gFields[i].c, gFields[i].name, gFields[i].ft);
67        }
68
69        for (size_t i = 0; i < NELEM(gMethods); ++i) {
70            *gMethods[i].id = GetMethodIDOrDie(env,
71                    *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
72        }
73    }
74
75    static jint writeEventInteger(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
76            jint tag, jint value) {
77        android_log_event_list ctx(tag);
78        ctx << (int32_t)value;
79        return ctx.write(LogID);
80    }
81    static jint writeEventLong(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
82            jint tag, jlong value) {
83        android_log_event_list ctx(tag);
84        ctx << (int64_t)value;
85        return ctx.write(LogID);
86    }
87    static jint writeEventFloat(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
88            jint tag, jfloat value) {
89        android_log_event_list ctx(tag);
90        ctx << (float)value;
91        return ctx.write(LogID);
92    }
93    static jint writeEventString(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
94            jstring value) {
95        android_log_event_list ctx(tag);
96        // Don't throw NPE -- I feel like it's sort of mean for a logging function
97        // to be all crashy if you pass in NULL -- but make the NULL value explicit.
98        ctx << (value != nullptr ? ScopedUtfChars(env, value).c_str() : "NULL");
99        return ctx.write(LogID);
100    }
101    static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
102            jobjectArray value) {
103        android_log_event_list ctx(tag);
104
105        if (value == nullptr) {
106            ctx << "[NULL]";
107            return ctx.write(LogID);
108        }
109
110        jsize copied = 0, num = env->GetArrayLength(value);
111        for (; copied < num && copied < 255; ++copied) {
112            if (ctx.status()) break;
113            ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(value, copied));
114            if (item == nullptr) {
115                ctx << "NULL";
116            } else if (env->IsInstanceOf(item.get(), gStringClass)) {
117                ctx << ScopedUtfChars(env, (jstring) item.get()).c_str();
118            } else if (env->IsInstanceOf(item.get(), gIntegerClass)) {
119                ctx << (int32_t)env->GetIntField(item.get(), gIntegerValueID);
120            } else if (env->IsInstanceOf(item.get(), gLongClass)) {
121                ctx << (int64_t)env->GetLongField(item.get(), gLongValueID);
122            } else if (env->IsInstanceOf(item.get(), gFloatClass)) {
123                ctx << (float)env->GetFloatField(item.get(), gFloatValueID);
124            } else {
125                jniThrowException(env,
126                        "java/lang/IllegalArgumentException",
127                        "Invalid payload item type");
128                return -1;
129            }
130        }
131        return ctx.write(LogID);
132    }
133
134    static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) {
135        readEvents(env, loggerMode, nullptr, startTime, out);
136    }
137
138    static void readEvents(JNIEnv* env, int loggerMode, jintArray jTags, jlong startTime,
139            jobject out) {
140        std::unique_ptr<struct logger_list, decltype(&android_logger_list_close)> logger_list(
141                nullptr, android_logger_list_close);
142        if (startTime) {
143            logger_list.reset(android_logger_list_alloc_time(loggerMode,
144                    log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0));
145        } else {
146            logger_list.reset(android_logger_list_alloc(loggerMode, 0, 0));
147        }
148        if (!logger_list) {
149            jniThrowIOException(env, errno);
150            return;
151        }
152
153        if (!android_logger_open(logger_list.get(), LogID)) {
154            jniThrowIOException(env, errno);
155            return;
156        }
157
158        ScopedIntArrayRO tags(env);
159        if (jTags != nullptr) {
160            tags.reset(jTags);
161        }
162
163        while (1) {
164            log_msg log_msg;
165            int ret = android_logger_list_read(logger_list.get(), &log_msg);
166
167            if (ret == 0) {
168                return;
169            }
170            if (ret < 0) {
171                if (ret == -EINTR) {
172                    continue;
173                }
174                if (ret == -EINVAL) {
175                    jniThrowException(env, "java/io/IOException", "Event too short");
176                } else if (ret != -EAGAIN) {
177                    jniThrowIOException(env, -ret);  // Will throw on return
178                }
179                return;
180            }
181
182            if (log_msg.id() != LogID) {
183                continue;
184            }
185
186            int32_t tag = * (int32_t *) log_msg.msg();
187
188            if (jTags != nullptr) {
189                bool found = false;
190                for (size_t i = 0; !found && i < tags.size(); ++i) {
191                    found = (tag == tags[i]);
192                }
193                if (!found) {
194                    continue;
195                }
196            }
197
198            jsize len = ret;
199            ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(len));
200            if (array == nullptr) {
201                return;
202            }
203
204            {
205                ScopedByteArrayRW bytes(env, array.get());
206                memcpy(bytes.get(), log_msg.buf, len);
207            }
208
209            ScopedLocalRef<jobject> event(env,
210                    env->NewObject(gEventClass, gEventInitID, array.get()));
211            if (event == nullptr) {
212                return;
213            }
214
215            env->CallBooleanMethod(out, gCollectionAddID, event.get());
216            if (env->ExceptionCheck() == JNI_TRUE) {
217                return;
218            }
219        }
220    }
221
222private:
223    static jclass gCollectionClass;
224    static jmethodID gCollectionAddID;
225
226    static jclass gEventClass;
227    static jmethodID gEventInitID;
228
229    static jclass gIntegerClass;
230    static jfieldID gIntegerValueID;
231
232    static jclass gLongClass;
233    static jfieldID gLongValueID;
234
235    static jclass gFloatClass;
236    static jfieldID gFloatValueID;
237
238    static jclass gStringClass;
239};
240
241// Explicit instantiation declarations.
242template <log_id_t LogID, const char* EventClassDescriptor>
243jclass EventLogHelper<LogID, EventClassDescriptor>::gCollectionClass;
244template <log_id_t LogID, const char* EventClassDescriptor>
245jmethodID EventLogHelper<LogID, EventClassDescriptor>::gCollectionAddID;
246
247template <log_id_t LogID, const char* EventClassDescriptor>
248jclass EventLogHelper<LogID, EventClassDescriptor>::gEventClass;
249template <log_id_t LogID, const char* EventClassDescriptor>
250jmethodID EventLogHelper<LogID, EventClassDescriptor>::gEventInitID;
251
252template <log_id_t LogID, const char* EventClassDescriptor>
253jclass EventLogHelper<LogID, EventClassDescriptor>::gIntegerClass;
254template <log_id_t LogID, const char* EventClassDescriptor>
255jfieldID EventLogHelper<LogID, EventClassDescriptor>::gIntegerValueID;
256
257template <log_id_t LogID, const char* EventClassDescriptor>
258jclass EventLogHelper<LogID, EventClassDescriptor>::gLongClass;
259template <log_id_t LogID, const char* EventClassDescriptor>
260jfieldID EventLogHelper<LogID, EventClassDescriptor>::gLongValueID;
261
262template <log_id_t LogID, const char* EventClassDescriptor>
263jclass EventLogHelper<LogID, EventClassDescriptor>::gFloatClass;
264template <log_id_t LogID, const char* EventClassDescriptor>
265jfieldID EventLogHelper<LogID, EventClassDescriptor>::gFloatValueID;
266
267template <log_id_t LogID, const char* EventClassDescriptor>
268jclass EventLogHelper<LogID, EventClassDescriptor>::gStringClass;
269
270}  // namespace android
271
272#endif  // FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
273