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_read.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 
36 namespace android {
37 
38 template <log_id_t LogID, const char* EventClassDescriptor>
39 class EventLogHelper {
40 public:
Init(JNIEnv * env)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 
writeEventInteger(JNIEnv * env ATTRIBUTE_UNUSED,jobject clazz ATTRIBUTE_UNUSED,jint tag,jint value)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     }
writeEventLong(JNIEnv * env ATTRIBUTE_UNUSED,jobject clazz ATTRIBUTE_UNUSED,jint tag,jlong value)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     }
writeEventFloat(JNIEnv * env ATTRIBUTE_UNUSED,jobject clazz ATTRIBUTE_UNUSED,jint tag,jfloat value)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     }
writeEventString(JNIEnv * env,jobject clazz ATTRIBUTE_UNUSED,jint tag,jstring value)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     }
writeEventArray(JNIEnv * env,jobject clazz ATTRIBUTE_UNUSED,jint tag,jobjectArray value)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 
readEvents(JNIEnv * env,int loggerMode,jlong startTime,jobject out)134     static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) {
135         readEvents(env, loggerMode, nullptr, startTime, out);
136     }
137 
readEvents(JNIEnv * env,int loggerMode,jintArray jTags,jlong startTime,jobject out)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 
222 private:
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.
242 template <log_id_t LogID, const char* EventClassDescriptor>
243 jclass EventLogHelper<LogID, EventClassDescriptor>::gCollectionClass;
244 template <log_id_t LogID, const char* EventClassDescriptor>
245 jmethodID EventLogHelper<LogID, EventClassDescriptor>::gCollectionAddID;
246 
247 template <log_id_t LogID, const char* EventClassDescriptor>
248 jclass EventLogHelper<LogID, EventClassDescriptor>::gEventClass;
249 template <log_id_t LogID, const char* EventClassDescriptor>
250 jmethodID EventLogHelper<LogID, EventClassDescriptor>::gEventInitID;
251 
252 template <log_id_t LogID, const char* EventClassDescriptor>
253 jclass EventLogHelper<LogID, EventClassDescriptor>::gIntegerClass;
254 template <log_id_t LogID, const char* EventClassDescriptor>
255 jfieldID EventLogHelper<LogID, EventClassDescriptor>::gIntegerValueID;
256 
257 template <log_id_t LogID, const char* EventClassDescriptor>
258 jclass EventLogHelper<LogID, EventClassDescriptor>::gLongClass;
259 template <log_id_t LogID, const char* EventClassDescriptor>
260 jfieldID EventLogHelper<LogID, EventClassDescriptor>::gLongValueID;
261 
262 template <log_id_t LogID, const char* EventClassDescriptor>
263 jclass EventLogHelper<LogID, EventClassDescriptor>::gFloatClass;
264 template <log_id_t LogID, const char* EventClassDescriptor>
265 jfieldID EventLogHelper<LogID, EventClassDescriptor>::gFloatValueID;
266 
267 template <log_id_t LogID, const char* EventClassDescriptor>
268 jclass EventLogHelper<LogID, EventClassDescriptor>::gStringClass;
269 
270 }  // namespace android
271 
272 #endif  // FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
273