1/*
2 * Copyright (C) 2007-2014 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#include <fcntl.h>
18
19#include <log/log_event_list.h>
20
21#include <log/log.h>
22
23#include "JNIHelp.h"
24#include "core_jni_helpers.h"
25#include "jni.h"
26
27#define UNUSED  __attribute__((__unused__))
28
29namespace android {
30
31static jclass gCollectionClass;
32static jmethodID gCollectionAddID;
33
34static jclass gEventClass;
35static jmethodID gEventInitID;
36
37static jclass gIntegerClass;
38static jfieldID gIntegerValueID;
39
40static jclass gLongClass;
41static jfieldID gLongValueID;
42
43static jclass gFloatClass;
44static jfieldID gFloatValueID;
45
46static jclass gStringClass;
47
48/*
49 * In class android.util.EventLog:
50 *  static native int writeEvent(int tag, int value)
51 */
52static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env UNUSED,
53                                                     jobject clazz UNUSED,
54                                                     jint tag, jint value)
55{
56    android_log_event_list ctx(tag);
57    ctx << (int32_t)value;
58    return ctx.write();
59}
60
61/*
62 * In class android.util.EventLog:
63 *  static native int writeEvent(long tag, long value)
64 */
65static jint android_util_EventLog_writeEvent_Long(JNIEnv* env UNUSED,
66                                                  jobject clazz UNUSED,
67                                                  jint tag, jlong value)
68{
69    android_log_event_list ctx(tag);
70    ctx << (int64_t)value;
71    return ctx.write();
72}
73
74/*
75 * In class android.util.EventLog:
76 *  static native int writeEvent(long tag, float value)
77 */
78static jint android_util_EventLog_writeEvent_Float(JNIEnv* env UNUSED,
79                                                  jobject clazz UNUSED,
80                                                  jint tag, jfloat value)
81{
82    android_log_event_list ctx(tag);
83    ctx << (float)value;
84    return ctx.write();
85}
86
87/*
88 * In class android.util.EventLog:
89 *  static native int writeEvent(int tag, String value)
90 */
91static jint android_util_EventLog_writeEvent_String(JNIEnv* env,
92                                                    jobject clazz UNUSED,
93                                                    jint tag, jstring value) {
94    android_log_event_list ctx(tag);
95    // Don't throw NPE -- I feel like it's sort of mean for a logging function
96    // to be all crashy if you pass in NULL -- but make the NULL value explicit.
97    if (value != NULL) {
98        const char *str = env->GetStringUTFChars(value, NULL);
99        ctx << str;
100        env->ReleaseStringUTFChars(value, str);
101    } else {
102        ctx << "NULL";
103    }
104    return ctx.write();
105}
106
107/*
108 * In class android.util.EventLog:
109 *  static native int writeEvent(long tag, Object... value)
110 */
111static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz,
112                                                   jint tag, jobjectArray value) {
113    android_log_event_list ctx(tag);
114
115    if (value == NULL) {
116        ctx << "[NULL]";
117        return ctx.write();
118    }
119
120    jsize copied = 0, num = env->GetArrayLength(value);
121    for (; copied < num && copied < 255; ++copied) {
122        if (ctx.status()) break;
123        jobject item = env->GetObjectArrayElement(value, copied);
124        if (item == NULL) {
125            ctx << "NULL";
126        } else if (env->IsInstanceOf(item, gStringClass)) {
127            const char *str = env->GetStringUTFChars((jstring) item, NULL);
128            ctx << str;
129            env->ReleaseStringUTFChars((jstring) item, str);
130        } else if (env->IsInstanceOf(item, gIntegerClass)) {
131            ctx << (int32_t)env->GetIntField(item, gIntegerValueID);
132        } else if (env->IsInstanceOf(item, gLongClass)) {
133            ctx << (int64_t)env->GetLongField(item, gLongValueID);
134        } else if (env->IsInstanceOf(item, gFloatClass)) {
135            ctx << (float)env->GetFloatField(item, gFloatValueID);
136        } else {
137            jniThrowException(env,
138                    "java/lang/IllegalArgumentException",
139                    "Invalid payload item type");
140            return -1;
141        }
142        env->DeleteLocalRef(item);
143    }
144    return ctx.write();
145}
146
147static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime, jobject out) {
148    struct logger_list *logger_list;
149    if (startTime) {
150        logger_list = android_logger_list_alloc_time(loggerMode,
151                log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
152    } else {
153        logger_list = android_logger_list_alloc(loggerMode, 0, 0);
154    }
155    if (!logger_list) {
156        jniThrowIOException(env, errno);
157        return;
158    }
159
160    if (!android_logger_open(logger_list, LOG_ID_EVENTS)) {
161        jniThrowIOException(env, errno);
162        android_logger_list_free(logger_list);
163        return;
164    }
165
166    jsize tagLength = env->GetArrayLength(tags);
167    jint *tagValues = env->GetIntArrayElements(tags, NULL);
168
169    while (1) {
170        log_msg log_msg;
171        int ret = android_logger_list_read(logger_list, &log_msg);
172
173        if (ret == 0) {
174            break;
175        }
176        if (ret < 0) {
177            if (ret == -EINTR) {
178                continue;
179            }
180            if (ret == -EINVAL) {
181                jniThrowException(env, "java/io/IOException", "Event too short");
182            } else if (ret != -EAGAIN) {
183                jniThrowIOException(env, -ret);  // Will throw on return
184            }
185            break;
186        }
187
188        if (log_msg.id() != LOG_ID_EVENTS) {
189            continue;
190        }
191
192        int32_t tag = * (int32_t *) log_msg.msg();
193
194        int found = 0;
195        for (int i = 0; !found && i < tagLength; ++i) {
196            found = (tag == tagValues[i]);
197        }
198
199        if (found) {
200            jsize len = ret;
201            jbyteArray array = env->NewByteArray(len);
202            if (array == NULL) {
203                break;
204            }
205
206            jbyte *bytes = env->GetByteArrayElements(array, NULL);
207            memcpy(bytes, log_msg.buf, len);
208            env->ReleaseByteArrayElements(array, bytes, 0);
209
210            jobject event = env->NewObject(gEventClass, gEventInitID, array);
211            if (event == NULL) {
212                break;
213            }
214
215            env->CallBooleanMethod(out, gCollectionAddID, event);
216            env->DeleteLocalRef(event);
217            env->DeleteLocalRef(array);
218        }
219    }
220
221    android_logger_list_close(logger_list);
222
223    env->ReleaseIntArrayElements(tags, tagValues, 0);
224}
225
226/*
227 * In class android.util.EventLog:
228 *  static native void readEvents(int[] tags, Collection<Event> output)
229 *
230 *  Reads events from the event log
231 */
232static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED,
233                                             jintArray tags,
234                                             jobject out) {
235
236    if (tags == NULL || out == NULL) {
237        jniThrowNullPointerException(env, NULL);
238        return;
239    }
240
241    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out);
242 }
243/*
244 * In class android.util.EventLog:
245 *  static native void readEventsOnWrapping(int[] tags, long timestamp, Collection<Event> output)
246 *
247 *  Reads events from the event log, blocking until events after timestamp are to be overwritten.
248 */
249static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz UNUSED,
250                                             jintArray tags,
251                                             jlong timestamp,
252                                             jobject out) {
253    if (tags == NULL || out == NULL) {
254        jniThrowNullPointerException(env, NULL);
255        return;
256    }
257    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP,
258            tags, timestamp, out);
259}
260
261/*
262 * JNI registration.
263 */
264static const JNINativeMethod gRegisterMethods[] = {
265    /* name, signature, funcPtr */
266    { "writeEvent", "(II)I", (void*) android_util_EventLog_writeEvent_Integer },
267    { "writeEvent", "(IJ)I", (void*) android_util_EventLog_writeEvent_Long },
268    { "writeEvent", "(IF)I", (void*) android_util_EventLog_writeEvent_Float },
269    { "writeEvent",
270      "(ILjava/lang/String;)I",
271      (void*) android_util_EventLog_writeEvent_String
272    },
273    { "writeEvent",
274      "(I[Ljava/lang/Object;)I",
275      (void*) android_util_EventLog_writeEvent_Array
276    },
277    { "readEvents",
278      "([ILjava/util/Collection;)V",
279      (void*) android_util_EventLog_readEvents
280    },
281    { "readEventsOnWrapping",
282      "([IJLjava/util/Collection;)V",
283      (void*) android_util_EventLog_readEventsOnWrapping
284    },
285};
286
287static struct { const char *name; jclass *clazz; } gClasses[] = {
288    { "android/util/EventLog$Event", &gEventClass },
289    { "java/lang/Integer", &gIntegerClass },
290    { "java/lang/Long", &gLongClass },
291    { "java/lang/Float", &gFloatClass },
292    { "java/lang/String", &gStringClass },
293    { "java/util/Collection", &gCollectionClass },
294};
295
296static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
297    { &gIntegerClass, "value", "I", &gIntegerValueID },
298    { &gLongClass, "value", "J", &gLongValueID },
299    { &gFloatClass, "value", "F", &gFloatValueID },
300};
301
302static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
303    { &gEventClass, "<init>", "([B)V", &gEventInitID },
304    { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
305};
306
307int register_android_util_EventLog(JNIEnv* env) {
308    for (int i = 0; i < NELEM(gClasses); ++i) {
309        jclass clazz = FindClassOrDie(env, gClasses[i].name);
310        *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
311    }
312
313    for (int i = 0; i < NELEM(gFields); ++i) {
314        *gFields[i].id = GetFieldIDOrDie(env,
315                *gFields[i].c, gFields[i].name, gFields[i].ft);
316    }
317
318    for (int i = 0; i < NELEM(gMethods); ++i) {
319        *gMethods[i].id = GetMethodIDOrDie(env,
320                *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
321    }
322
323    return RegisterMethodsOrDie(
324            env,
325            "android/util/EventLog",
326            gRegisterMethods, NELEM(gRegisterMethods));
327}
328
329}; // namespace android
330