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