lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl \
lowpan/java/android/net/lowpan/ILowpanManager.aidl
+# StatsLog generated functions
+statslog_src_dir := $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)/statslog
+gen := $(statslog_src_dir)/android/util/StatsLog.java
+$(gen): PRIVATE_PATH := $(LOCAL_PATH)
+$(gen): PRIVATE_CUSTOM_TOOL = $(HOST_OUT_EXECUTABLES)/stats-log-api-gen --java $@
+$(gen): $(HOST_OUT_EXECUTABLES)/stats-log-api-gen
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(gen)
+statslog_src_dir:=
+gen:=
+
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
LOCAL_AIDL_INCLUDES += \
$(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \
--- /dev/null
+//
+// Copyright (C) 2015 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.
+//
+
+// ==========================================================
+// Build the library for use on the host
+// ==========================================================
+cc_library_host_shared {
+ name: "libstats_proto_host",
+ srcs: [
+ "src/stats_events.proto",
+ "src/stats_log.proto",
+ "src/statsd_config.proto",
+ ],
+
+ shared_libs: [
+ "libplatformprotos",
+ ],
+
+ proto: {
+ type: "full",
+ export_proto_headers: true,
+ },
+}
+
LOCAL_PATH:= $(call my-dir)
-# ================
-# proto static lib
-# ================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := statsd_proto
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-proto-files-under, src)
+statsd_common_src := \
+ ../../core/java/android/os/IStatsCompanionService.aidl \
+ ../../core/java/android/os/IStatsManager.aidl \
+ src/stats_log.proto \
+ src/statsd_config.proto \
+ src/stats_events.proto \
+ src/condition/CombinationConditionTracker.cpp \
+ src/condition/condition_util.cpp \
+ src/condition/SimpleConditionTracker.cpp \
+ src/matchers/CombinationLogMatchingTracker.cpp \
+ src/matchers/matcher_util.cpp \
+ src/matchers/SimpleLogMatchingTracker.cpp \
+ src/metrics/CountAnomalyTracker.cpp \
+ src/metrics/CountMetricProducer.cpp \
+ src/metrics/MetricsManager.cpp \
+ src/metrics/metrics_manager_util.cpp \
+ src/AnomalyMonitor.cpp \
+ src/DropboxReader.cpp \
+ src/DropboxWriter.cpp \
+ src/KernelWakelockPuller.cpp \
+ src/LogEntryPrinter.cpp \
+ src/LogReader.cpp \
+ src/StatsLogProcessor.cpp \
+ src/StatsPullerManager.cpp \
+ src/StatsService.cpp \
+ src/stats_util.cpp \
+ src/UidMap.cpp
-LOCAL_PROTOC_FLAGS :=
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
+statsd_common_c_includes := \
+ $(LOCAL_PATH)/src
-include $(BUILD_STATIC_LIBRARY)
+statsd_common_aidl_includes := \
+ $(LOCAL_PATH)/../../core/java
-STATSD_PROTO_INCLUDES := $(local-generated-sources-dir)/src/$(LOCAL_PATH)
+statsd_common_shared_libraries := \
+ libbase \
+ libbinder \
+ libcutils \
+ libincident \
+ liblog \
+ libselinux \
+ libutils \
+ libservices \
+ libandroidfw
# =========
# statsd
LOCAL_MODULE := statsd
LOCAL_SRC_FILES := \
- ../../core/java/android/os/IStatsCompanionService.aidl \
- ../../core/java/android/os/IStatsManager.aidl \
- $(call all-cpp-files-under,src) \
+ $(statsd_common_src) \
+ src/main.cpp
LOCAL_CFLAGS += \
-Wall \
LOCAL_CFLAGS += \
-Os
endif
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
- STATSD_PROTO_INCLUDES
-
-LOCAL_STATIC_LIBRARIES := statsd_proto
+LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
+LOCAL_C_INCLUDES += $(statsd_common_c_includes)
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libbinder \
- libcutils \
- libincident \
- liblog \
- libselinux \
- libutils \
- libservices \
- libandroidfw \
- libprotobuf-cpp-lite \
+LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
LOCAL_MODULE_CLASS := EXECUTABLES
include $(BUILD_EXECUTABLE)
+
# ==============
# statsd_test
# ==============
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
- STATSD_PROTO_INCLUDES
+LOCAL_AIDL_INCLUDES := $(statsd_common_c_includes)
+LOCAL_C_INCLUDES += $(statsd_common_c_includes)
LOCAL_CFLAGS += \
-Wall \
-Wno-unused-parameter
LOCAL_SRC_FILES := \
- src/stats_log.proto \
- src/statsd_config.proto \
- ../../core/java/android/os/IStatsCompanionService.aidl \
- ../../core/java/android/os/IStatsManager.aidl \
- src/StatsService.cpp \
- src/AnomalyMonitor.cpp \
- src/stats_util.cpp \
- src/LogEntryPrinter.cpp \
- src/LogReader.cpp \
- src/matchers/matcher_util.cpp \
- src/condition/SimpleConditionTracker.cpp \
- src/condition/CombinationConditionTracker.cpp \
- src/matchers/SimpleLogMatchingTracker.cpp \
- src/matchers/CombinationLogMatchingTracker.cpp \
- src/metrics/metrics_manager_util.cpp \
- src/metrics/CountMetricProducer.cpp \
- src/metrics/CountAnomalyTracker.cpp \
- src/condition/condition_util.cpp \
- src/UidMap.cpp \
- $(call all-cpp-files-under, tests) \
+ $(statsd_common_src) \
+ tests/indexed_priority_queue_test.cpp \
+ tests/LogReader_test.cpp \
+ tests/MetricsManager_test.cpp \
+ tests/UidMap_test.cpp \
+ tests/LogEntryMatcher_test.cpp \
+ tests/AnomalyMonitor_test.cpp \
+ tests/ConditionTracker_test.cpp
LOCAL_STATIC_LIBRARIES := \
- libgmock \
- statsd_proto \
+ libgmock
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libbinder \
- libcutils \
- liblog \
- libselinux \
- libutils \
- libprotobuf-cpp-lite \
+LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+
+statsd_common_src:=
+statsd_common_aidl_includes:=
+statsd_common_c_includes:=
include $(BUILD_NATIVE_TEST)
+
#include <log/logprint.h>
#include <utils/Errors.h>
+#include "matchers/matcher_util.h"
+
+#define PRINT_WITH_LIBLOG 0
+#define PRINT_WITH_LOG_EVENT_WRAPPER 1
+
using namespace android;
namespace android {
}
void LogEntryPrinter::OnLogEvent(const log_msg& msg) {
- status_t err;
- AndroidLogEntry entry;
- char buf[1024];
-
- err = android_log_processBinaryLogBuffer(&(const_cast<log_msg*>(&msg)->entry_v1), &entry,
- m_tags, buf, sizeof(buf));
- if (err == NO_ERROR) {
- android_log_printLogLine(m_format, m_out, &entry);
- } else {
- printf("log entry: %s\n", buf);
+ if (PRINT_WITH_LIBLOG) {
+ status_t err;
+ AndroidLogEntry entry;
+ char buf[1024];
+
+ err = android_log_processBinaryLogBuffer(&(const_cast<log_msg*>(&msg)->entry_v1), &entry,
+ m_tags, buf, sizeof(buf));
+ if (err == NO_ERROR) {
+ android_log_printLogLine(m_format, m_out, &entry);
+ } else {
+ printf("log entry: %s\n", buf);
+ fflush(stdout);
+ }
+ }
+
+ if (PRINT_WITH_LOG_EVENT_WRAPPER) {
+ LogEventWrapper event = parseLogEvent(msg);
+ printf("event: %s\n", event.toString().c_str());
fflush(stdout);
}
}
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
+#include <sstream>
+
using std::set;
using std::string;
+using std::ostringstream;
using std::unordered_map;
using std::vector;
namespace os {
namespace statsd {
+string LogEventWrapper::toString() const {
+ std::ostringstream result;
+ result << "{ " << timestamp_ns << " (" << tagId << ")";
+ for (int index = 1; ; index++) {
+ auto intVal = intMap.find(index);
+ auto strVal = strMap.find(index);
+ auto boolVal = boolMap.find(index);
+ auto floatVal = floatMap.find(index);
+ if (intVal != intMap.end()) {
+ result << " ";
+ result << std::to_string(index);
+ result << "->";
+ result << std::to_string(intVal->second);
+ } else if (strVal != strMap.end()) {
+ result << " ";
+ result << std::to_string(index);
+ result << "->";
+ result << strVal->second;
+ } else if (boolVal != boolMap.end()) {
+ result << " ";
+ result << std::to_string(index);
+ result << "->";
+ result << std::to_string(boolVal->second);
+ } else if (floatVal != floatMap.end()) {
+ result << " ";
+ result << std::to_string(index);
+ result << "->";
+ result << std::to_string(floatVal->second);
+ } else {
+ break;
+ }
+ }
+ result << " }";
+ return result.str();
+}
+
LogEventWrapper parseLogEvent(log_msg msg) {
LogEventWrapper wrapper;
wrapper.timestamp_ns = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
if (context) {
memset(&elem, 0, sizeof(elem));
- size_t index = 0;
- int32_t key = -1;
+ // TODO: The log is actually structured inside one list. This is convenient
+ // because we'll be able to use it to put the attribution (WorkSource) block first
+ // without doing our own tagging scheme. Until that change is in, just drop the
+ // list-related log elements and the order we get there is our index-keyed data
+ // structure.
+ int32_t key = 1;
do {
elem = android_log_read_next(context);
switch ((int)elem.type) {
case EVENT_TYPE_INT:
- if (index % 2 == 0) {
- key = elem.data.int32;
- } else {
- wrapper.intMap[key] = elem.data.int32;
- }
- index++;
+ wrapper.intMap[key] = elem.data.int32;
+ key++;
break;
case EVENT_TYPE_FLOAT:
- if (index % 2 == 1) {
- wrapper.floatMap[key] = elem.data.float32;
- }
- index++;
+ wrapper.floatMap[key] = elem.data.float32;
+ key++;
break;
case EVENT_TYPE_STRING:
- if (index % 2 == 1) {
- // without explicit calling string() constructor, there will be an
- // additional 0 in the end of the string.
- wrapper.strMap[key] = string(elem.data.string);
- }
- index++;
+ // without explicit calling string() constructor, there will be an
+ // additional 0 in the end of the string.
+ wrapper.strMap[key] = string(elem.data.string);
+ key++;
break;
case EVENT_TYPE_LONG:
- if (index % 2 == 1) {
- wrapper.intMap[key] = elem.data.int64;
- }
- index++;
+ wrapper.intMap[key] = elem.data.int64;
+ key++;
break;
case EVENT_TYPE_LIST:
break;
elem.complete = true;
break;
}
-
- if (elem.complete) {
- break;
- }
} while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
android_log_destroy(&context);
#include <log/log_read.h>
#include <log/logprint.h>
#include <set>
+#include <string>
#include <unordered_map>
#include <vector>
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
std::unordered_map<int, std::string> strMap;
std::unordered_map<int, bool> boolMap;
std::unordered_map<int, float> floatMap;
+
+ std::string toString() const;
} LogEventWrapper;
enum MatchingState {
*/
syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
+// TODO: Not the right package and class name
package android.os.statsd;
-
option java_package = "com.android.os";
option java_outer_classname = "StatsEventProto";
+/**
+ * The master event class. This message defines all of the available
+ * raw stats log events from the Android system, also known as "atoms."
+ *
+ * This field contains a single oneof with all of the available messages.
+ * The stats-log-api-gen tool runs as part of the Android build and
+ * generates the android.util.StatsLog class, which contains the constants
+ * and methods that Android uses to log.
+ *
+ * This StatsEvent class is not actually built into the Android system.
+ * Instead, statsd on Android constructs these messages synthetically,
+ * in the format defined here and in stats_log.proto.
+ */
message StatsEvent {
- oneof event {
- // Screen state change.
- ScreenStateChange screen_state_change = 2;
- // Process state change.
- ProcessStateChange process_state_change = 1112;
- }
+ oneof event {
+ ScreenStateChanged screen_state_changed = 1;
+ ProcessStateChanged process_state_changed = 2;
+ WakeLockChanged wakelock_changed = 3;
+ }
+}
+
+/**
+ * A WorkSource represents the chained attribution of applications that
+ * resulted in a particular bit of work being done.
+ */
+message WorkSource {
+ // TODO
+}
+
+/*
+ * *****************************************************************************
+ * Below are all of the individual atoms that are logged by Android via statsd
+ * and Westworld.
+ *
+ * RULES:
+ * - The field ids for each atom must start at 1, and count upwards by 1.
+ * Skipping field ids is not allowed.
+ * - These form an API, so renaming, renumbering or removing fields is
+ * not allowed between android releases. (This is not currently enforced,
+ * but there will be a tool to enforce this restriction).
+ * - The types must be built-in protocol buffer types, namely, no sub-messages
+ * are allowed (yet). The bytes type is also not allowed.
+ * - The CamelCase name of the message type should match the
+ * underscore_separated name as defined in StatsEvent.
+ * - If an atom represents work that can be attributed to an app, there can
+ * be exactly one WorkSource field. It must be field number 1.
+ * - A field that is a uid should be a string field, tagged with the [xxx]
+ * annotation. The generated code on android will be represented by UIDs,
+ * and those UIDs will be translated in xxx to those strings.
+ *
+ * CONVENTIONS:
+ * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange
+ * - If there is a UID, it goes first. Think in an object-oriented fashion.
+ * *****************************************************************************
+ */
+
+/**
+ * Logs when the screen state changes.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message ScreenStateChanged {
+ // TODO: Use the real screen state.
+ enum State {
+ STATE_UNKNOWN = 0;
+ STATE_OFF = 1;
+ STATE_ON = 2;
+ STATE_DOZE = 3;
+ STATE_DOZE_SUSPEND = 4;
+ STATE_VR = 5;
+ }
+ // New screen state.
+ optional State display_state = 1;
}
-// Logs changes in screen state. This event is logged in
-// frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
-message ScreenStateChange {
- // Screen state enums follow the values defined in below file.
- // frameworks/base/core/java/android/view/Display.java
- enum State {
- STATE_UNKNOWN = 0;
- STATE_OFF = 1;
- STATE_ON = 2;
- STATE_DOZE = 3;
- STATE_DOZE_SUSPEND = 4;
- STATE_VR = 5;
- }
- // New screen state.
- optional State display_state = 1;
+/**
+ * Logs that the state of a process state, as per the activity manager has changed.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ */
+message ProcessStateChanged {
+ // TODO: Use the real (mapped) process states.
+ optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
+
+ // The state.
+ optional int32 state = 2;
}
-// Logs changes in process state. This event is logged in
-// frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
-message ProcessStateChange {
- // Type of process event.
- enum State {
- START = 1;
- CRASH = 2;
- }
- optional State state = 1;
-
- // UID associated with the package.
- optional int32 uid = 2;
+/**
+ * Logs that the state of a wakelock has changed.
+ *
+ * Logged from:
+ * TODO
+ */
+message WakeLockChanged {
+ // TODO: Add attribution instead of uid.
+ optional int32 uid = 1;
+
+ // The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
+ optional string tag = 2;
+
+ // TODO: Use a constant instead of boolean?
+ optional bool state = 3;
}
+
+++ /dev/null
-/*
- * Copyright (C) 2007 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.
- */
-
-package android.util;
-
-/**
- * Logging access for platform metrics.
- *
- * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
- * These diagnostic stats are for system integrators, not application authors.
- *
- * <p>Stats use integer tag codes.
- * They carry a payload of one or more int, long, or String values.
- * @hide
- */
-public class StatsLog {
- /** @hide */ public StatsLog() {}
-
- private static final String TAG = "StatsLog";
-
- // We assume that the native methods deal with any concurrency issues.
-
- /**
- * Records an stats log message.
- * @param tag The stats type tag code
- * @param value A value to log
- * @return The number of bytes written
- */
- public static native int writeInt(int tag, int value);
-
- /**
- * Records an stats log message.
- * @param tag The stats type tag code
- * @param value A value to log
- * @return The number of bytes written
- */
- public static native int writeLong(int tag, long value);
-
- /**
- * Records an stats log message.
- * @param tag The stats type tag code
- * @param value A value to log
- * @return The number of bytes written
- */
- public static native int writeFloat(int tag, float value);
-
- /**
- * Records an stats log message.
- * @param tag The stats type tag code
- * @param str A value to log
- * @return The number of bytes written
- */
- public static native int writeString(int tag, String str);
-
- /**
- * Records an stats log message.
- * @param tag The stats type tag code
- * @param list A list of values to log. All values should
- * be of type int, long, float or String.
- * @return The number of bytes written
- */
- public static native int writeArray(int tag, Object... list);
-}
+++ /dev/null
-/*
- * 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.
- */
-
-// THIS FILE IS AUTO-GENERATED.
-// DO NOT MODIFY.
-
-package android.util;
-
-/** @hide */
-public class StatsLogKey {
- private StatsLogKey() {}
-
- /** Constants for android.os.statsd.ScreenStateChange. */
-
- /** display_state */
- public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE = 1;
-
- /** Constants for android.os.statsd.ProcessStateChange. */
-
- /** state */
- public static final int PROCESS_STATE_CHANGE__STATE = 1;
-
- /** uid */
- public static final int PROCESS_STATE_CHANGE__UID = 2;
-
- /** package_name */
- public static final int PROCESS_STATE_CHANGE__PACKAGE_NAME = 1002;
-
- /** package_version */
- public static final int PROCESS_STATE_CHANGE__PACKAGE_VERSION = 3;
-
- /** package_version_string */
- public static final int PROCESS_STATE_CHANGE__PACKAGE_VERSION_STRING = 4;
-
-}
+++ /dev/null
-/*
- * 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.
- */
-
-// THIS FILE IS AUTO-GENERATED.
-// DO NOT MODIFY.
-
-package android.util;
-
-/** @hide */
-public class StatsLogTag {
- private StatsLogTag() {}
-
- /** android.os.statsd.ScreenStateChange. */
- public static final int SCREEN_STATE_CHANGE = 2;
-
- /** android.os.statsd.ProcessStateChange. */
- public static final int PROCESS_STATE_CHANGE = 1112;
-
-}
+++ /dev/null
-/*
- * 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.
- */
-
-// THIS FILE IS AUTO-GENERATED.
-// DO NOT MODIFY.
-
-package android.util;
-
-/** @hide */
-public class StatsLogValue {
- private StatsLogValue() {}
-
- /** Constants for android.os.statsd.ScreenStateChange. */
-
- /** display_state: STATE_UNKNOWN */
- public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_UNKNOWN = 0;
-
- /** display_state: STATE_OFF */
- public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF = 1;
-
- /** display_state: STATE_ON */
- public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON = 2;
-
- /** display_state: STATE_DOZE */
- public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_DOZE = 3;
-
- /** display_state: STATE_DOZE_SUSPEND */
- public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_DOZE_SUSPEND = 4;
-
- /** display_state: STATE_VR */
- public static final int SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_VR = 5;
-
- /** Constants for android.os.statsd.ProcessStateChange. */
-
- /** state: START */
- public static final int PROCESS_STATE_CHANGE__STATE__START = 1;
-
- /** state: CRASH */
- public static final int PROCESS_STATE_CHANGE__STATE__CRASH = 2;
-
-}
+
+genrule {
+ name: "android_util_StatsLog.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --jni $(genDir)/android_util_StatsLog.cpp",
+ out: [
+ "android_util_StatsLog.cpp",
+ ],
+}
+
cc_library_shared {
name: "libandroid_runtime",
"android_nio_utils.cpp",
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
- "android_util_StatsLog.cpp",
"android_util_EventLog.cpp",
"android_util_MemoryIntArray.cpp",
"android_util_Log.cpp",
"libhwbinder",
"libvintf",
"libnativewindow",
-
"libhwui",
"libdl",
+ "libstatslog",
],
+ generated_sources: ["android_util_StatsLog.cpp"],
+
local_include_dirs: ["android/graphics"],
export_include_dirs: [
".",
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
- REG_JNI(register_android_util_StatsLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_MemoryIntArray),
REG_JNI(register_android_util_PathParser),
+ REG_JNI(register_android_util_StatsLog),
REG_JNI(register_android_app_admin_SecurityLog),
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
+++ /dev/null
-/*
- * Copyright (C) 2007-2014 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.
- */
-
-#include <fcntl.h>
-#include <log/log_event_list.h>
-
-#include <log/log.h>
-
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include "jni.h"
-
-#define UNUSED __attribute__((__unused__))
-
-namespace android {
-
-static jclass gCollectionClass;
-static jmethodID gCollectionAddID;
-
-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.StatsLog:
- * static native int writeInt(int tag, int value)
- */
-static jint android_util_StatsLog_write_Integer(JNIEnv* env UNUSED,
- jobject clazz UNUSED,
- jint tag, jint value)
-{
- android_log_event_list ctx(tag);
- ctx << (int32_t)value;
- return ctx.write(LOG_ID_STATS);
-}
-
-/*
- * In class android.util.StatsLog:
- * static native int writeLong(long tag, long value)
- */
-static jint android_util_StatsLog_write_Long(JNIEnv* env UNUSED,
- jobject clazz UNUSED,
- jint tag, jlong value)
-{
- android_log_event_list ctx(tag);
- ctx << (int64_t)value;
- return ctx.write(LOG_ID_STATS);
-}
-
-/*
- * In class android.util.StatsLog:
- * static native int writeFloat(long tag, float value)
- */
-static jint android_util_StatsLog_write_Float(JNIEnv* env UNUSED,
- jobject clazz UNUSED,
- jint tag, jfloat value)
-{
- android_log_event_list ctx(tag);
- ctx << (float)value;
- return ctx.write(LOG_ID_STATS);
-}
-
-/*
- * In class android.util.StatsLog:
- * static native int writeString(int tag, String value)
- */
-static jint android_util_StatsLog_write_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(LOG_ID_STATS);
-}
-
-/*
- * In class android.util.StatsLog:
- * static native int writeArray(long tag, Object... value)
- */
-static jint android_util_StatsLog_write_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_STATS);
- }
-
- 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_STATS);
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gRegisterMethods[] = {
- /* name, signature, funcPtr */
- { "writeInt", "(II)I", (void*) android_util_StatsLog_write_Integer },
- { "writeLong", "(IJ)I", (void*) android_util_StatsLog_write_Long },
- { "writeFloat", "(IF)I", (void*) android_util_StatsLog_write_Float },
- { "writeString",
- "(ILjava/lang/String;)I",
- (void*) android_util_StatsLog_write_String
- },
- { "writeArray",
- "(I[Ljava/lang/Object;)I",
- (void*) android_util_StatsLog_write_Array
- },
-};
-
-static struct { const char *name; jclass *clazz; } gClasses[] = {
- { "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[] = {
- { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
-};
-
-int register_android_util_StatsLog(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);
- }
-
- return RegisterMethodsOrDie(
- env,
- "android/util/StatsLog",
- gRegisterMethods, NELEM(gRegisterMethods));
-}
-
-}; // namespace android
void noteProcessStart(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessStartLocked(name, uid);
-
- // TODO: remove this once we figure out properly where and how
- // PROCESS_EVENT = 1112
- // KEY_STATE = 1
- // KEY_PACKAGE_NAME: 1002
- // KEY_UID: 2
- StatsLog.writeArray(1112, 1, 1, 1002, name, 2, uid);
}
}
void noteProcessCrash(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessCrashLocked(name, uid);
-
- // TODO: remove this once we figure out properly where and how
- // PROCESS_EVENT = 1112
- // KEY_STATE = 1
- // KEY_PACKAGE_NAME: 1002
- // KEY_UID: 2
- StatsLog.writeArray(1112, 1, 2, 1002, name, 2, uid);
}
}
void noteUidProcessState(int uid, int state) {
synchronized (mStats) {
+ // TODO: remove this once we figure out properly where and how
+ StatsLog.write(StatsLog.PROCESS_STATE_CHANGED, uid, state);
+
mStats.noteUidProcessStateLocked(uid, state);
}
}
enforceCallingPermission();
if (DBG) Slog.d(TAG, "begin noteScreenState");
synchronized (mStats) {
- mStats.noteScreenStateLocked(state);
// TODO: remove this once we figure out properly where and how
- // SCREEN_EVENT = 2
- // KEY_STATE: 1
- // State value: state. We can change this to our own def later.
- StatsLog.writeArray(2, 1, state);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, state);
+
+ mStats.noteScreenStateLocked(state);
}
if (DBG) Slog.d(TAG, "end noteScreenState");
}
--- /dev/null
+//
+// 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.
+//
+
+// ==========================================================
+// Build the host executable: stats-log-api-gen
+// ==========================================================
+cc_binary_host {
+ name: "stats-log-api-gen",
+ srcs: [
+ "Collation.cpp",
+ "main.cpp",
+ ],
+
+ shared_libs: [
+ "libstats_proto_host",
+ "libprotobuf-cpp-full",
+ ],
+
+ proto: {
+ type: "full",
+ },
+}
+
+// ==========================================================
+// Build the host test executable: stats-log-api-gen
+// ==========================================================
+cc_test_host {
+ name: "stats-log-api-gen-test",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-g",
+ "-DUNIT_TEST",
+ ],
+ srcs: [
+ "Collation.cpp",
+ "test_collation.cpp",
+ "test.proto",
+ ],
+
+ static_libs: [
+ "libgmock_host",
+ ],
+
+ shared_libs: [
+ "libstats_proto_host",
+ ],
+
+ proto: {
+ type: "full",
+ },
+}
+
+// ==========================================================
+// Native library
+// ==========================================================
+genrule {
+ name: "statslog.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog.h",
+ out: [
+ "statslog.h",
+ ],
+}
+
+genrule {
+ name: "statslog.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog.cpp",
+ out: [
+ "statslog.cpp",
+ ],
+}
+
+cc_library_shared {
+ name: "libstatslog",
+ generated_sources: ["statslog.cpp"],
+ generated_headers: ["statslog.h"],
+ export_generated_headers: ["statslog.h"],
+ shared_libs: [
+ "liblog",
+ ],
+}
+
--- /dev/null
+/*
+ * 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.
+ */
+
+#include "Collation.h"
+
+#include <stdio.h>
+#include <map>
+
+namespace android {
+namespace stats_log_api_gen {
+
+using google::protobuf::FieldDescriptor;
+using google::protobuf::FileDescriptor;
+using google::protobuf::SourceLocation;
+using std::map;
+
+
+//
+// AtomDecl class
+//
+
+AtomDecl::AtomDecl()
+ :code(0),
+ name()
+{
+}
+
+AtomDecl::AtomDecl(const AtomDecl& that)
+ :code(that.code),
+ name(that.name),
+ message(that.message),
+ fields(that.fields)
+{
+}
+
+AtomDecl::AtomDecl(int c, const string& n, const string& m)
+ :code(c),
+ name(n),
+ message(m)
+{
+}
+
+AtomDecl::~AtomDecl()
+{
+}
+
+
+/**
+ * Print an error message for a FieldDescriptor, including the file name and line number.
+ */
+static void
+print_error(const FieldDescriptor* field, const char* format, ...)
+{
+ const Descriptor* message = field->containing_type();
+ const FileDescriptor* file = message->file();
+
+ SourceLocation loc;
+ if (field->GetSourceLocation(&loc)) {
+ // TODO: this will work if we can figure out how to pass --include_source_info to protoc
+ fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
+ } else {
+ fprintf(stderr, "%s: ", file->name().c_str());
+ }
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end (args);
+}
+
+/**
+ * Convert a protobuf type into a java type.
+ */
+static java_type_t
+java_type(const FieldDescriptor* field)
+{
+ int protoType = field->type();
+ switch (protoType) {
+ case FieldDescriptor::TYPE_DOUBLE:
+ return JAVA_TYPE_DOUBLE;
+ case FieldDescriptor::TYPE_FLOAT:
+ return JAVA_TYPE_FLOAT;
+ case FieldDescriptor::TYPE_INT64:
+ return JAVA_TYPE_LONG;
+ case FieldDescriptor::TYPE_UINT64:
+ return JAVA_TYPE_LONG;
+ case FieldDescriptor::TYPE_INT32:
+ return JAVA_TYPE_INT;
+ case FieldDescriptor::TYPE_FIXED64:
+ return JAVA_TYPE_LONG;
+ case FieldDescriptor::TYPE_FIXED32:
+ return JAVA_TYPE_INT;
+ case FieldDescriptor::TYPE_BOOL:
+ return JAVA_TYPE_BOOLEAN;
+ case FieldDescriptor::TYPE_STRING:
+ return JAVA_TYPE_STRING;
+ case FieldDescriptor::TYPE_GROUP:
+ return JAVA_TYPE_UNKNOWN;
+ case FieldDescriptor::TYPE_MESSAGE:
+ // TODO: not the final package name
+ if (field->message_type()->full_name() == "android.os.statsd.WorkSource") {
+ return JAVA_TYPE_WORK_SOURCE;
+ } else {
+ return JAVA_TYPE_OBJECT;
+ }
+ case FieldDescriptor::TYPE_BYTES:
+ return JAVA_TYPE_BYTE_ARRAY;
+ case FieldDescriptor::TYPE_UINT32:
+ return JAVA_TYPE_INT;
+ case FieldDescriptor::TYPE_ENUM:
+ return JAVA_TYPE_INT;
+ case FieldDescriptor::TYPE_SFIXED32:
+ return JAVA_TYPE_INT;
+ case FieldDescriptor::TYPE_SFIXED64:
+ return JAVA_TYPE_LONG;
+ case FieldDescriptor::TYPE_SINT32:
+ return JAVA_TYPE_INT;
+ case FieldDescriptor::TYPE_SINT64:
+ return JAVA_TYPE_LONG;
+ default:
+ return JAVA_TYPE_UNKNOWN;
+ }
+}
+
+/**
+ * Gather the info about the atoms.
+ */
+int
+collate_atoms(const Descriptor* descriptor, Atoms* atoms)
+{
+ int errorCount = 0;
+ const bool dbg = false;
+
+ for (int i=0; i<descriptor->field_count(); i++) {
+ const FieldDescriptor* atomField = descriptor->field(i);
+
+ if (dbg) {
+ printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
+ }
+
+ // StatsEvent only has one oneof, which contains only messages. Don't allow other types.
+ if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
+ print_error(atomField,
+ "Bad type for atom. StatsEvent can only have message type fields: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ }
+
+ const Descriptor* atom = atomField->message_type();
+
+ // Build a sorted list of the fields. Descriptor has them in source file order.
+ map<int,const FieldDescriptor*> fields;
+ for (int j=0; j<atom->field_count(); j++) {
+ const FieldDescriptor* field = atom->field(j);
+ fields[field->number()] = field;
+ }
+
+ // Check that the parameters start at 1 and go up sequentially.
+ int expectedNumber = 1;
+ for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const int number = it->first;
+ const FieldDescriptor* field = it->second;
+ if (number != expectedNumber) {
+ print_error(field, "Fields must be numbered consecutively starting at 1:"
+ " '%s' is %d but should be %d\n",
+ field->name().c_str(), number, expectedNumber);
+ errorCount++;
+ expectedNumber = number;
+ continue;
+ }
+ expectedNumber++;
+ }
+
+ // Check that only allowed types are present. Remove any invalid ones.
+ for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const FieldDescriptor* field = it->second;
+
+ java_type_t javaType = java_type(field);
+
+ if (javaType == JAVA_TYPE_UNKNOWN) {
+ print_error(field, "Unkown type for field: %s\n", field->name().c_str());
+ errorCount++;
+ continue;
+ } else if (javaType == JAVA_TYPE_OBJECT) {
+ // Allow WorkSources, but only at position 1.
+ print_error(field, "Message type not allowed for field: %s\n",
+ field->name().c_str());
+ errorCount++;
+ continue;
+ } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
+ print_error(field, "Raw bytes type not allowed for field: %s\n",
+ field->name().c_str());
+ errorCount++;
+ continue;
+ }
+
+ }
+
+ // Check that if there's a WorkSource, it's at position 1.
+ for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ int number = it->first;
+ if (number != 1) {
+ const FieldDescriptor* field = it->second;
+ java_type_t javaType = java_type(field);
+ if (javaType == JAVA_TYPE_WORK_SOURCE) {
+ print_error(field, "WorkSource fields must have field id 1, in message: '%s'\n",
+ atom->name().c_str());
+ errorCount++;
+ }
+ }
+ }
+
+ AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
+
+ // Build the type signature
+ vector<java_type_t> signature;
+ for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const FieldDescriptor* field = it->second;
+ java_type_t javaType = java_type(field);
+
+ atomDecl.fields.push_back(AtomField(field->name(), javaType));
+ signature.push_back(javaType);
+ }
+
+ atoms->signatures.insert(signature);
+ atoms->decls.insert(atomDecl);
+ }
+
+ if (dbg) {
+ printf("signatures = [\n");
+ for (set<vector<java_type_t>>::const_iterator it = atoms->signatures.begin();
+ it != atoms->signatures.end(); it++) {
+ printf(" ");
+ for (vector<java_type_t>::const_iterator jt = it->begin(); jt != it->end(); jt++) {
+ printf(" %d", (int)*jt);
+ }
+ printf("\n");
+ }
+ printf("]\n");
+ }
+
+ return errorCount;
+}
+
+} // namespace stats_log_api_gen
+} // namespace android
+
+
--- /dev/null
+/*
+ * 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 ANDROID_STATS_LOG_API_GEN_COLLATION_H
+#define ANDROID_STATS_LOG_API_GEN_COLLATION_H
+
+
+#include <google/protobuf/descriptor.h>
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace stats_log_api_gen {
+
+using std::set;
+using std::string;
+using std::vector;
+using google::protobuf::Descriptor;
+
+/**
+ * The types for atom parameters.
+ */
+typedef enum {
+ JAVA_TYPE_UNKNOWN = 0,
+
+ JAVA_TYPE_WORK_SOURCE = 1,
+ JAVA_TYPE_BOOLEAN = 2,
+ JAVA_TYPE_INT = 3,
+ JAVA_TYPE_LONG = 4,
+ JAVA_TYPE_FLOAT = 5,
+ JAVA_TYPE_DOUBLE = 6,
+ JAVA_TYPE_STRING = 7,
+
+ JAVA_TYPE_OBJECT = -1,
+ JAVA_TYPE_BYTE_ARRAY = -2,
+} java_type_t;
+
+
+/**
+ * The name and type for an atom field.
+ */
+struct AtomField {
+ string name;
+ java_type_t javaType;
+
+ inline AtomField() :name(), javaType(JAVA_TYPE_UNKNOWN) {}
+ inline AtomField(const AtomField& that) :name(that.name), javaType(that.javaType) {}
+ inline AtomField(string n, java_type_t jt) :name(n), javaType(jt) {}
+ inline ~AtomField() {}
+};
+
+/**
+ * The name and code for an atom.
+ */
+struct AtomDecl {
+ int code;
+ string name;
+
+ string message;
+ vector<AtomField> fields;
+
+ AtomDecl();
+ AtomDecl(const AtomDecl& that);
+ AtomDecl(int code, const string& name, const string& message);
+ ~AtomDecl();
+
+ inline bool operator<(const AtomDecl& that) const {
+ return (code == that.code) ? (name < that.name) : (code < that.code);
+ }
+};
+
+struct Atoms {
+ set<vector<java_type_t>> signatures;
+ set<AtomDecl> decls;
+};
+
+/**
+ * Gather the information about the atoms. Returns the number of errors.
+ */
+int collate_atoms(const Descriptor* descriptor, Atoms* atoms);
+
+} // namespace stats_log_api_gen
+} // namespace android
+
+
+#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
--- /dev/null
+
+
+#include "Collation.h"
+
+#include "frameworks/base/cmds/statsd/src/stats_events.pb.h"
+
+#include <set>
+#include <vector>
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+using namespace google::protobuf;
+using namespace std;
+
+namespace android {
+namespace stats_log_api_gen {
+
+using android::os::statsd::StatsEvent;
+
+// TODO: Support WorkSources
+
+/**
+ * Turn lower and camel case into upper case with underscores.
+ */
+static string
+make_constant_name(const string& str)
+{
+ string result;
+ const int N = str.size();
+ bool underscore_next = false;
+ for (int i=0; i<N; i++) {
+ char c = str[i];
+ if (c >= 'A' && c <= 'Z') {
+ if (underscore_next) {
+ result += '_';
+ underscore_next = false;
+ }
+ } else if (c >= 'a' && c <= 'z') {
+ c = 'A' + c - 'a';
+ underscore_next = true;
+ } else if (c == '_') {
+ underscore_next = false;
+ }
+ result += c;
+ }
+ return result;
+}
+
+static const char*
+cpp_type_name(java_type_t type)
+{
+ switch (type) {
+ case JAVA_TYPE_BOOLEAN:
+ return "bool";
+ case JAVA_TYPE_INT:
+ return "int32_t";
+ case JAVA_TYPE_LONG:
+ return "int64_t";
+ case JAVA_TYPE_FLOAT:
+ return "float";
+ case JAVA_TYPE_DOUBLE:
+ return "double";
+ case JAVA_TYPE_STRING:
+ return "char const*";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static const char*
+java_type_name(java_type_t type)
+{
+ switch (type) {
+ case JAVA_TYPE_BOOLEAN:
+ return "boolean";
+ case JAVA_TYPE_INT:
+ return "int";
+ case JAVA_TYPE_LONG:
+ return "long";
+ case JAVA_TYPE_FLOAT:
+ return "float";
+ case JAVA_TYPE_DOUBLE:
+ return "double";
+ case JAVA_TYPE_STRING:
+ return "java.lang.String";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int
+write_stats_log_cpp(FILE* out, const Atoms& atoms)
+{
+ int errorCount;
+
+ // Print prelude
+ fprintf(out, "// This file is autogenerated\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "#include <log/log_event_list.h>\n");
+ fprintf(out, "#include <log/log.h>\n");
+ fprintf(out, "#include <statslog.h>\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "namespace android {\n");
+ fprintf(out, "namespace util {\n");
+
+ // Print write methods
+ fprintf(out, "\n");
+ for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+ signature != atoms.signatures.end(); signature++) {
+ int argIndex;
+
+ fprintf(out, "void\n");
+ fprintf(out, "stats_write(int code");
+ argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ argIndex++;
+ }
+ fprintf(out, ")\n");
+
+ fprintf(out, "{\n");
+ argIndex = 1;
+ fprintf(out, " android_log_event_list event(code);\n");
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_STRING) {
+ fprintf(out, " if (arg%d == NULL) {\n", argIndex);
+ fprintf(out, " arg%d = \"\";\n", argIndex);
+ fprintf(out, " }\n");
+ }
+ fprintf(out, " event << arg%d;\n", argIndex);
+ argIndex++;
+ }
+
+ fprintf(out, " event.write(LOG_ID_STATS);\n");
+ fprintf(out, "}\n");
+ fprintf(out, "\n");
+ }
+
+ // Print footer
+ fprintf(out, "\n");
+ fprintf(out, "} // namespace util\n");
+ fprintf(out, "} // namespace android\n");
+
+ return 0;
+}
+
+
+static int
+write_stats_log_header(FILE* out, const Atoms& atoms)
+{
+ int errorCount;
+
+ // Print prelude
+ fprintf(out, "// This file is autogenerated\n");
+ fprintf(out, "\n");
+ fprintf(out, "#pragma once\n");
+ fprintf(out, "\n");
+ fprintf(out, "#include <stdint.h>\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "namespace android {\n");
+ fprintf(out, "namespace util {\n");
+ fprintf(out, "\n");
+ fprintf(out, "/*\n");
+ fprintf(out, " * API For logging statistics events.\n");
+ fprintf(out, " */\n");
+ fprintf(out, "\n");
+ fprintf(out, "/**\n");
+ fprintf(out, " * Constants for event codes.\n");
+ fprintf(out, " */\n");
+ fprintf(out, "enum {\n");
+
+ size_t i = 0;
+ // Print constants
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ string constant = make_constant_name(atom->name);
+ fprintf(out, "\n");
+ fprintf(out, " /**\n");
+ fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
+ fprintf(out, " * Usage: stats_write(StatsLog.%s", constant.c_str());
+ for (vector<AtomField>::const_iterator field = atom->fields.begin();
+ field != atom->fields.end(); field++) {
+ fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+ }
+ fprintf(out, ");\n");
+ fprintf(out, " */\n");
+ char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
+ fprintf(out, " %s = %d%s\n", constant.c_str(), atom->code, comma);
+ i++;
+ }
+ fprintf(out, "\n");
+ fprintf(out, "};\n");
+ fprintf(out, "\n");
+
+ // Print write methods
+ fprintf(out, "//\n");
+ fprintf(out, "// Write methods\n");
+ fprintf(out, "//\n");
+ for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+ signature != atoms.signatures.end(); signature++) {
+
+ fprintf(out, "void stats_write(int code");
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ argIndex++;
+ }
+ fprintf(out, ");\n");
+ }
+
+ fprintf(out, "\n");
+ fprintf(out, "} // namespace util\n");
+ fprintf(out, "} // namespace android\n");
+
+ return 0;
+}
+
+static int
+write_stats_log_java(FILE* out, const Atoms& atoms)
+{
+ int errorCount;
+
+ // Print prelude
+ fprintf(out, "// This file is autogenerated\n");
+ fprintf(out, "\n");
+ fprintf(out, "package android.util;\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "/**\n");
+ fprintf(out, " * API For logging statistics events.\n");
+ fprintf(out, " * @hide\n");
+ fprintf(out, " */\n");
+ fprintf(out, "public final class StatsLog {\n");
+ fprintf(out, " // Constants for event codes.\n");
+
+ // Print constants
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ string constant = make_constant_name(atom->name);
+ fprintf(out, "\n");
+ fprintf(out, " /**\n");
+ fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
+ fprintf(out, " * Usage: StatsLog.write(StatsLog.%s", constant.c_str());
+ for (vector<AtomField>::const_iterator field = atom->fields.begin();
+ field != atom->fields.end(); field++) {
+ fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+ }
+ fprintf(out, ");\n");
+ fprintf(out, " */\n");
+ fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
+ }
+ fprintf(out, "\n");
+
+ // Print write methods
+ fprintf(out, " // Write methods\n");
+ for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+ signature != atoms.signatures.end(); signature++) {
+ fprintf(out, " public static native void write(int code");
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ argIndex++;
+ }
+ fprintf(out, ");\n");
+ }
+
+ fprintf(out, "}\n");
+
+ return 0;
+}
+
+static const char*
+jni_type_name(java_type_t type)
+{
+ switch (type) {
+ case JAVA_TYPE_BOOLEAN:
+ return "jboolean";
+ case JAVA_TYPE_INT:
+ return "jint";
+ case JAVA_TYPE_LONG:
+ return "jlong";
+ case JAVA_TYPE_FLOAT:
+ return "jfloat";
+ case JAVA_TYPE_DOUBLE:
+ return "jdouble";
+ case JAVA_TYPE_STRING:
+ return "jstring";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static string
+jni_function_name(const vector<java_type_t>& signature)
+{
+ string result("StatsLog_write");
+ for (vector<java_type_t>::const_iterator arg = signature.begin();
+ arg != signature.end(); arg++) {
+ switch (*arg) {
+ case JAVA_TYPE_BOOLEAN:
+ result += "_boolean";
+ break;
+ case JAVA_TYPE_INT:
+ result += "_int";
+ break;
+ case JAVA_TYPE_LONG:
+ result += "_long";
+ break;
+ case JAVA_TYPE_FLOAT:
+ result += "_float";
+ break;
+ case JAVA_TYPE_DOUBLE:
+ result += "_double";
+ break;
+ case JAVA_TYPE_STRING:
+ result += "_String";
+ break;
+ default:
+ result += "_UNKNOWN";
+ break;
+ }
+ }
+ return result;
+}
+
+static const char*
+java_type_signature(java_type_t type)
+{
+ switch (type) {
+ case JAVA_TYPE_BOOLEAN:
+ return "Z";
+ case JAVA_TYPE_INT:
+ return "I";
+ case JAVA_TYPE_LONG:
+ return "J";
+ case JAVA_TYPE_FLOAT:
+ return "F";
+ case JAVA_TYPE_DOUBLE:
+ return "D";
+ case JAVA_TYPE_STRING:
+ return "Ljava/lang/String;";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static string
+jni_function_signature(const vector<java_type_t>& signature)
+{
+ string result("(I");
+ for (vector<java_type_t>::const_iterator arg = signature.begin();
+ arg != signature.end(); arg++) {
+ result += java_type_signature(*arg);
+ }
+ result += ")V";
+ return result;
+}
+
+static int
+write_stats_log_jni(FILE* out, const Atoms& atoms)
+{
+ int errorCount;
+
+ // Print prelude
+ fprintf(out, "// This file is autogenerated\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "#include <statslog.h>\n");
+ fprintf(out, "\n");
+ fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
+ fprintf(out, "#include \"core_jni_helpers.h\"\n");
+ fprintf(out, "#include \"jni.h\"\n");
+ fprintf(out, "\n");
+ fprintf(out, "#define UNUSED __attribute__((__unused__))\n");
+ fprintf(out, "\n");
+
+ fprintf(out, "namespace android {\n");
+ fprintf(out, "\n");
+
+ // Print write methods
+ for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+ signature != atoms.signatures.end(); signature++) {
+ int argIndex;
+
+ fprintf(out, "static void\n");
+ fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
+ jni_function_name(*signature).c_str());
+ argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+ argIndex++;
+ }
+ fprintf(out, ")\n");
+
+ fprintf(out, "{\n");
+
+ // Prepare strings
+ argIndex = 1;
+ bool hadString = false;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_STRING) {
+ fprintf(out, " const char* str%d;\n", argIndex);
+ fprintf(out, " if (arg%d != NULL) {\n", argIndex);
+ fprintf(out, " str%d = env->GetStringUTFChars(arg%d, NULL);\n",
+ argIndex, argIndex);
+ fprintf(out, " } else {\n");
+ fprintf(out, " str%d = NULL;\n", argIndex);
+ fprintf(out, " }\n");
+ hadString = true;
+ }
+ argIndex++;
+ }
+
+ // Emit this to quiet the unused parameter warning if there were no strings.
+ if (!hadString) {
+ fprintf(out, " (void)env;\n");
+ }
+
+ // stats_write call
+ argIndex = 1;
+ fprintf(out, " android::util::stats_write(code");
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ const char* argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
+ fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+ argIndex++;
+ }
+ fprintf(out, ");\n");
+
+ // Clean up strings
+ argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature->begin();
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_STRING) {
+ fprintf(out, " if (str%d != NULL) {\n", argIndex);
+ fprintf(out, " env->ReleaseStringUTFChars(arg%d, str%d);\n",
+ argIndex, argIndex);
+ fprintf(out, " }\n");
+ }
+ argIndex++;
+ }
+
+ fprintf(out, "}\n");
+ fprintf(out, "\n");
+ }
+
+ // Print registration function table
+ fprintf(out, "/*\n");
+ fprintf(out, " * JNI registration.\n");
+ fprintf(out, " */\n");
+ fprintf(out, "static const JNINativeMethod gRegisterMethods[] = {\n");
+ for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+ signature != atoms.signatures.end(); signature++) {
+ fprintf(out, " { \"write\", \"%s\", (void*)%s },\n",
+ jni_function_signature(*signature).c_str(),
+ jni_function_name(*signature).c_str());
+ }
+ fprintf(out, "};\n");
+ fprintf(out, "\n");
+
+ // Print registration function
+ fprintf(out, "int register_android_util_StatsLog(JNIEnv* env) {\n");
+ fprintf(out, " return RegisterMethodsOrDie(\n");
+ fprintf(out, " env,\n");
+ fprintf(out, " \"android/util/StatsLog\",\n");
+ fprintf(out, " gRegisterMethods, NELEM(gRegisterMethods));\n");
+ fprintf(out, "}\n");
+
+ fprintf(out, "\n");
+ fprintf(out, "} // namespace android\n");
+
+ return 0;
+}
+
+
+static void
+print_usage()
+{
+ fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "OPTIONS\n");
+ fprintf(stderr, " --cpp FILENAME the header file to output\n");
+ fprintf(stderr, " --header FILENAME the cpp file to output\n");
+ fprintf(stderr, " --help this message\n");
+ fprintf(stderr, " --java FILENAME the java file to output\n");
+ fprintf(stderr, " --jni FILENAME the jni file to output\n");
+}
+
+/**
+ * Do the argument parsing and execute the tasks.
+ */
+static int
+run(int argc, char const*const* argv)
+{
+ string cppFilename;
+ string headerFilename;
+ string javaFilename;
+ string jniFilename;
+
+ int index = 1;
+ while (index < argc) {
+ if (0 == strcmp("--help", argv[index])) {
+ print_usage();
+ return 0;
+ } else if (0 == strcmp("--cpp", argv[index])) {
+ index++;
+ if (index >= argc) {
+ print_usage();
+ return 1;
+ }
+ cppFilename = argv[index];
+ } else if (0 == strcmp("--header", argv[index])) {
+ index++;
+ if (index >= argc) {
+ print_usage();
+ return 1;
+ }
+ headerFilename = argv[index];
+ } else if (0 == strcmp("--java", argv[index])) {
+ index++;
+ if (index >= argc) {
+ print_usage();
+ return 1;
+ }
+ javaFilename = argv[index];
+ } else if (0 == strcmp("--jni", argv[index])) {
+ index++;
+ if (index >= argc) {
+ print_usage();
+ return 1;
+ }
+ jniFilename = argv[index];
+ }
+ index++;
+ }
+
+ if (cppFilename.size() == 0
+ && headerFilename.size() == 0
+ && javaFilename.size() == 0
+ && jniFilename.size() == 0) {
+ print_usage();
+ return 1;
+ }
+
+ // Collate the parameters
+ Atoms atoms;
+ int errorCount = collate_atoms(StatsEvent::descriptor(), &atoms);
+ if (errorCount != 0) {
+ return 1;
+ }
+
+ // Write the .cpp file
+ if (cppFilename.size() != 0) {
+ FILE* out = fopen(cppFilename.c_str(), "w");
+ if (out == NULL) {
+ fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
+ return 1;
+ }
+ errorCount = android::stats_log_api_gen::write_stats_log_cpp(out, atoms);
+ fclose(out);
+ }
+
+ // Write the .h file
+ if (headerFilename.size() != 0) {
+ FILE* out = fopen(headerFilename.c_str(), "w");
+ if (out == NULL) {
+ fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
+ return 1;
+ }
+ errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms);
+ fclose(out);
+ }
+
+ // Write the .java file
+ if (javaFilename.size() != 0) {
+ FILE* out = fopen(javaFilename.c_str(), "w");
+ if (out == NULL) {
+ fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
+ return 1;
+ }
+ errorCount = android::stats_log_api_gen::write_stats_log_java(out, atoms);
+ fclose(out);
+ }
+
+ // Write the jni file
+ if (jniFilename.size() != 0) {
+ FILE* out = fopen(jniFilename.c_str(), "w");
+ if (out == NULL) {
+ fprintf(stderr, "Unable to open file for write: %s\n", jniFilename.c_str());
+ return 1;
+ }
+ errorCount = android::stats_log_api_gen::write_stats_log_jni(out, atoms);
+ fclose(out);
+ }
+
+ return 0;
+}
+
+}
+}
+
+/**
+ * Main.
+ */
+int
+main(int argc, char const*const* argv)
+{
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ return android::stats_log_api_gen::run(argc, argv);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/cmds/statsd/src/stats_events.proto";
+
+package android.stats_log_api_gen;
+
+message IntAtom {
+ optional int32 field1 = 1;
+}
+
+message AnotherIntAtom {
+ optional int32 field1 = 1;
+}
+
+message OutOfOrderAtom {
+ optional int32 field2 = 2;
+ optional int32 field1 = 1;
+}
+
+enum AnEnum {
+ VALUE0 = 0;
+ VALUE1 = 1;
+}
+
+message AllTypesAtom {
+ optional android.os.statsd.WorkSource attribution = 1;
+ optional double double_field = 2;
+ optional float float_field = 3;
+ optional int64 int64_field = 4;
+ optional uint64 uint64_field = 5;
+ optional int32 int32_field = 6;
+ optional fixed64 fixed64_field = 7;
+ optional fixed32 fixed32_field = 8;
+ optional bool bool_field = 9;
+ optional string string_field = 10;
+ optional uint32 uint32_field = 11;
+ optional AnEnum enum_field = 12;
+ optional sfixed32 sfixed32_field = 13;
+ optional sfixed64 sfixed64_field = 14;
+ optional sint32 sint32_field = 15;
+ optional sint64 sint64_field = 16;
+}
+
+message Event {
+ oneof event {
+ OutOfOrderAtom out_of_order_atom = 2;
+ IntAtom int_atom = 1;
+ AnotherIntAtom another_int_atom = 3;
+ AllTypesAtom all_types_atom = 4;
+ }
+}
+
+message BadTypesAtom {
+ optional IntAtom bad_int_atom = 1;
+ optional bytes bad_bytes = 2;
+}
+
+message BadTypesEvent {
+ oneof event {
+ BadTypesAtom bad_types_atom = 1;
+ }
+}
+
+message BadSkippedFieldSingleAtom {
+ optional int32 field2 = 2;
+}
+
+message BadSkippedFieldSingle {
+ oneof event {
+ BadSkippedFieldSingleAtom bad = 1;
+ }
+}
+
+message BadSkippedFieldMultipleAtom {
+ optional int32 field1 = 1;
+ optional int32 field3 = 3;
+ optional int32 field5 = 5;
+}
+
+message BadSkippedFieldMultiple {
+ oneof event {
+ BadSkippedFieldMultipleAtom bad = 1;
+ }
+}
+
+message BadWorkSourcePositionAtom {
+ optional int32 field1 = 1;
+ optional android.os.statsd.WorkSource attribution = 2;
+}
+
+message BadWorkSourcePosition {
+ oneof event {
+ BadWorkSourcePositionAtom bad = 1;
+ }
+}
+
--- /dev/null
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
+#include "Collation.h"
+
+#include <stdio.h>
+
+namespace android {
+namespace stats_log_api_gen {
+
+using std::set;
+using std::vector;
+
+/**
+ * Return whether the set contains a vector of the elements provided.
+ */
+static bool
+set_contains_vector(const set<vector<java_type_t>>& s, int count, ...)
+{
+ va_list args;
+ vector<java_type_t> v;
+
+ va_start(args, count);
+ for (int i=0; i<count; i++) {
+ v.push_back((java_type_t)va_arg(args, int));
+ }
+ va_end(args);
+
+ return s.find(v) != s.end();
+}
+
+/**
+ * Expect that the provided set contains the elements provided.
+ */
+#define EXPECT_SET_CONTAINS_SIGNATURE(s, ...) \
+ do { \
+ int count = sizeof((int[]){__VA_ARGS__})/sizeof(int); \
+ EXPECT_TRUE(set_contains_vector(s, count, __VA_ARGS__)); \
+ } while(0)
+
+/**
+ * Test a correct collation, with all the types.
+ */
+TEST(CollationTest, CollateStats) {
+ Atoms atoms;
+ int errorCount = collate_atoms(Event::descriptor(), &atoms);
+
+ EXPECT_EQ(0, errorCount);
+ EXPECT_EQ(3ul, atoms.signatures.size());
+
+ // IntAtom, AnotherIntAtom
+ EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures, JAVA_TYPE_INT);
+
+ // OutOfOrderAtom
+ EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures, JAVA_TYPE_INT, JAVA_TYPE_INT);
+
+ // AllTypesAtom
+ EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures,
+ JAVA_TYPE_WORK_SOURCE, // WorkSource
+ JAVA_TYPE_DOUBLE, // double
+ JAVA_TYPE_FLOAT, // float
+ JAVA_TYPE_LONG, // int64
+ JAVA_TYPE_LONG, // uint64
+ JAVA_TYPE_INT, // int32
+ JAVA_TYPE_LONG, // fixed64
+ JAVA_TYPE_INT, // fixed32
+ JAVA_TYPE_BOOLEAN, // bool
+ JAVA_TYPE_STRING, // string
+ JAVA_TYPE_INT, // uint32
+ JAVA_TYPE_INT, // AnEnum
+ JAVA_TYPE_INT, // sfixed32
+ JAVA_TYPE_LONG, // sfixed64
+ JAVA_TYPE_INT, // sint32
+ JAVA_TYPE_LONG // sint64
+ );
+
+ set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ EXPECT_EQ(1, atom->code);
+ EXPECT_EQ("int_atom", atom->name);
+ EXPECT_EQ("IntAtom", atom->message);
+ atom++;
+
+ EXPECT_EQ(2, atom->code);
+ EXPECT_EQ("out_of_order_atom", atom->name);
+ EXPECT_EQ("OutOfOrderAtom", atom->message);
+ atom++;
+
+ EXPECT_EQ(3, atom->code);
+ EXPECT_EQ("another_int_atom", atom->name);
+ EXPECT_EQ("AnotherIntAtom", atom->message);
+ atom++;
+
+ EXPECT_EQ(4, atom->code);
+ EXPECT_EQ("all_types_atom", atom->name);
+ EXPECT_EQ("AllTypesAtom", atom->message);
+ atom++;
+
+ EXPECT_TRUE(atom == atoms.decls.end());
+}
+
+/**
+ * Test that event class that contains stuff other than the atoms is rejected.
+ */
+TEST(CollationTest, NonMessageTypeFails) {
+ Atoms atoms;
+ int errorCount = collate_atoms(IntAtom::descriptor(), &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+/**
+ * Test that atoms that have non-primitve types are rejected.
+ */
+TEST(CollationTest, FailOnBadTypes) {
+ Atoms atoms;
+ int errorCount = collate_atoms(BadTypesEvent::descriptor(), &atoms);
+
+ EXPECT_EQ(2, errorCount);
+}
+
+/**
+ * Test that atoms that skip field numbers (in the first position) are rejected.
+ */
+TEST(CollationTest, FailOnSkippedFieldsSingle) {
+ Atoms atoms;
+ int errorCount = collate_atoms(BadSkippedFieldSingle::descriptor(), &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+/**
+ * Test that atoms that skip field numbers (not in the first position, and multiple
+ * times) are rejected.
+ */
+TEST(CollationTest, FailOnSkippedFieldsMultiple) {
+ Atoms atoms;
+ int errorCount = collate_atoms(BadSkippedFieldMultiple::descriptor(), &atoms);
+
+ EXPECT_EQ(2, errorCount);
+}
+
+/**
+ * Test that atoms that have a WorkSource not in the first position are rejected.
+ */
+TEST(CollationTest, FailBadWorkSourcePosition) {
+ Atoms atoms;
+ int errorCount = collate_atoms(BadWorkSourcePosition::descriptor(), &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+
+} // namespace stats_log_api_gen
+} // namespace android
+