summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Yao Chen <yaochen@google.com> 2018-12-16 19:00:11 -0800
committer android-build-merger <android-build-merger@google.com> 2018-12-16 19:00:11 -0800
commitd090104b51c97da01bd64a809048d56b73556bb0 (patch)
tree3e03ee27b7829f6704417b713ccb496b636c85d7
parentadcb8a46ae7bd4a02376582fcb477c54e1d143fe (diff)
parent393e1ba2e2ae30387c1b835bf17ff67e5dbc2127 (diff)
Merge changes from topic "statsd-allow-bytes-type" am: 783ec04909
am: 393e1ba2e2 Change-Id: I2840072c4986cc0fb5ed4a79b0bfa7994bcc1448
-rw-r--r--cmds/statsd/src/atom_field_options.proto12
-rw-r--r--cmds/statsd/src/atoms.proto12
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp2
-rw-r--r--cmds/statsd/src/stats_log_util.cpp42
-rw-r--r--cmds/statsd/tests/LogEvent_test.cpp95
-rw-r--r--core/proto/android/stats/launcher/launcher.proto86
-rw-r--r--tools/stats_log_api_gen/Collation.cpp25
-rw-r--r--tools/stats_log_api_gen/Collation.h2
-rw-r--r--tools/stats_log_api_gen/main.cpp106
-rw-r--r--tools/stats_log_api_gen/test.proto22
-rw-r--r--tools/stats_log_api_gen/test_collation.cpp14
11 files changed, 403 insertions, 15 deletions
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index a2a03b14c073..7dfe7d6ecb7e 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -64,10 +64,22 @@ message StateAtomFieldOption {
optional StateField option = 1 [default = STATE_FIELD_UNSET];
}
+// Used to generate StatsLog.write APIs.
+enum LogMode {
+ MODE_UNSET = 0;
+ // Log fields as their actual types e.g., all primary data types.
+ // Or fields that are hardcoded in stats_log_api_gen tool e.g., AttributionNode
+ MODE_AUTOMATIC = 1;
+ // Log fields in their proto binary format. These fields will not be parsed in statsd
+ MODE_BYTES = 2;
+}
+
extend google.protobuf.FieldOptions {
// Flags to decorate an atom that presents a state change.
optional StateAtomFieldOption stateFieldOption = 50000;
// Flags to decorate the uid fields in an atom.
optional bool is_uid = 50001 [default = false];
+
+ optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC];
} \ No newline at end of file
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2242129338fa..da9adff29b40 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -26,6 +26,7 @@ import "frameworks/base/core/proto/android/app/job/enums.proto";
import "frameworks/base/core/proto/android/bluetooth/enums.proto";
import "frameworks/base/core/proto/android/os/enums.proto";
import "frameworks/base/core/proto/android/server/enums.proto";
+import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
@@ -59,7 +60,8 @@ message Atom {
LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12;
WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13;
- // 14 - 19 are available
+ // 14 - 18 are available
+ LauncherUIChanged launcher_event = 19;
BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
@@ -1185,6 +1187,14 @@ message PhoneStateChanged {
optional State state = 1;
}
+message LauncherUIChanged {
+ optional android.stats.launcher.LauncherAction action = 1;
+ optional android.stats.launcher.LauncherState src_state = 2;
+ optional android.stats.launcher.LauncherState dst_state = 3;
+ optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES];
+ optional bool is_swipe_up_enabled = 5;
+}
+
/**
* Logs that a setting was updated.
* Logged from:
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 4e4f146d27ac..5d6d02b4948b 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -379,7 +379,7 @@ float LogEvent::GetFloat(size_t key, status_t* err) const {
string LogEvent::ToString() const {
string result;
- result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs,
+ result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
(long long)mElapsedTimestampNs, mTagId);
for (const auto& value : mValues) {
result +=
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a0ab3e46e719..11ce71705fc1 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -25,15 +25,16 @@
#include <utils/Log.h>
#include <utils/SystemClock.h>
+using android::util::AtomsInfo;
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
+using android::util::FIELD_TYPE_FIXED64;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_UINT64;
-using android::util::FIELD_TYPE_FIXED64;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::FIELD_TYPE_STRING;
+using android::util::FIELD_TYPE_UINT64;
using android::util::ProtoOutputStream;
namespace android {
@@ -294,8 +295,9 @@ void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
// }
//
//
-void writeFieldValueTreeToStreamHelper(const std::vector<FieldValue>& dims, size_t* index,
- int depth, int prefix, ProtoOutputStream* protoOutput) {
+void writeFieldValueTreeToStreamHelper(int tagId, const std::vector<FieldValue>& dims,
+ size_t* index, int depth, int prefix,
+ ProtoOutputStream* protoOutput) {
size_t count = dims.size();
while (*index < count) {
const auto& dim = dims[*index];
@@ -319,9 +321,33 @@ void writeFieldValueTreeToStreamHelper(const std::vector<FieldValue>& dims, size
case FLOAT:
protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
break;
- case STRING:
- protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
+ case STRING: {
+ bool isBytesField = false;
+ // Bytes field is logged via string format in log_msg format. So here we check
+ // if this string field is a byte field.
+ std::map<int, std::vector<int>>::const_iterator itr;
+ if (depth == 0 && (itr = AtomsInfo::kBytesFieldAtoms.find(tagId)) !=
+ AtomsInfo::kBytesFieldAtoms.end()) {
+ const std::vector<int>& bytesFields = itr->second;
+ for (int bytesField : bytesFields) {
+ if (bytesField == fieldNum) {
+ // This is a bytes field
+ isBytesField = true;
+ break;
+ }
+ }
+ }
+ if (isBytesField) {
+ if (dim.mValue.str_value.length() > 0) {
+ protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+ (const char*)dim.mValue.str_value.c_str(),
+ dim.mValue.str_value.length());
+ }
+ } else {
+ protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
+ }
break;
+ }
default:
break;
}
@@ -337,7 +363,7 @@ void writeFieldValueTreeToStreamHelper(const std::vector<FieldValue>& dims, size
}
// Directly jump to the leaf value because the repeated position field is implied
// by the position of the sub msg in the parent field.
- writeFieldValueTreeToStreamHelper(dims, index, valueDepth,
+ writeFieldValueTreeToStreamHelper(tagId, dims, index, valueDepth,
dim.mField.getPrefix(valueDepth), protoOutput);
if (msg_token != 0) {
protoOutput->end(msg_token);
@@ -354,7 +380,7 @@ void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& value
uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId);
size_t index = 0;
- writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput);
+ writeFieldValueTreeToStreamHelper(tagId, values, &index, 0, 0, protoOutput);
protoOutput->end(atomToken);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 2fcde29fbbdb..b29de5385cf4 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "src/logd/LogEvent.h"
#include <gtest/gtest.h>
#include <log/log_event_list.h>
-#include "src/logd/LogEvent.h"
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+#include "frameworks/base/core/proto/android/stats/launcher/launcher.pb.h"
#ifdef __ANDROID__
@@ -22,6 +24,9 @@ namespace android {
namespace os {
namespace statsd {
+using std::string;
+using util::ProtoOutputStream;
+
TEST(LogEventTest, TestLogParsing) {
LogEvent event1(1, 2000);
@@ -159,6 +164,94 @@ TEST(LogEventTest, TestLogParsing2) {
}
+TEST(LogEventTest, TestBinaryFieldAtom) {
+ Atom launcherAtom;
+ auto launcher_event = launcherAtom.mutable_launcher_event();
+ launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
+ launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
+ launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
+
+ auto extension = launcher_event->mutable_extension();
+
+ auto src_target = extension->add_src_target();
+ src_target->set_type(stats::launcher::LauncherTarget_Type_ITEM_TYPE);
+ src_target->set_item(stats::launcher::LauncherTarget_Item_FOLDER_ICON);
+
+ auto dst_target = extension->add_dst_target();
+ dst_target->set_type(stats::launcher::LauncherTarget_Type_ITEM_TYPE);
+ dst_target->set_item(stats::launcher::LauncherTarget_Item_WIDGET);
+
+ string extension_str;
+ extension->SerializeToString(&extension_str);
+
+ LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
+
+ event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
+ event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
+ event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
+ event1.write(extension_str);
+ event1.init();
+
+ ProtoOutputStream proto;
+ event1.ToProto(proto);
+
+ std::vector<uint8_t> outData;
+ outData.resize(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
+
+ std::string result_str(outData.begin(), outData.end());
+ std::string orig_str;
+ launcherAtom.SerializeToString(&orig_str);
+
+ EXPECT_EQ(orig_str, result_str);
+}
+
+TEST(LogEventTest, TestBinaryFieldAtom_empty) {
+ Atom launcherAtom;
+ auto launcher_event = launcherAtom.mutable_launcher_event();
+ launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
+ launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
+ launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
+
+ // empty string.
+ string extension_str;
+
+ LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
+
+ event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
+ event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
+ event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
+ event1.write(extension_str);
+ event1.init();
+
+ ProtoOutputStream proto;
+ event1.ToProto(proto);
+
+ std::vector<uint8_t> outData;
+ outData.resize(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
+
+ std::string result_str(outData.begin(), outData.end());
+ std::string orig_str;
+ launcherAtom.SerializeToString(&orig_str);
+
+ EXPECT_EQ(orig_str, result_str);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/core/proto/android/stats/launcher/launcher.proto b/core/proto/android/stats/launcher/launcher.proto
new file mode 100644
index 000000000000..dbd0e038c40c
--- /dev/null
+++ b/core/proto/android/stats/launcher/launcher.proto
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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";
+package android.stats.launcher;
+option java_multiple_files = true;
+
+enum LauncherAction {
+ DEFAULT_ACTION = 0;
+ LAUNCH_APP = 1;
+ LAUNCH_TASK = 2;
+ DISMISS_TASK = 3;
+ LONGPRESS = 4;
+ DRAGDROP = 5;
+ SWIPE_UP = 6;
+ SWIPE_DOWN = 7;
+ SWIPE_LEFT = 8;
+ SWIPE_RIGHT = 9;
+}
+
+enum LauncherState {
+ BACKGROUND = 0;
+ HOME = 1;
+ OVERVIEW = 2;
+ ALLAPPS = 3;
+}
+
+message LauncherTarget {
+ enum Type {
+ NONE = 0;
+ ITEM_TYPE = 1;
+ CONTROL_TYPE = 2;
+ CONTAINER_TYPE = 3;
+ }
+ enum Item {
+ DEFAULT_ITEM = 0;
+ APP_ICON = 1;
+ SHORTCUT = 2;
+ WIDGET = 3;
+ FOLDER_ICON = 4;
+ DEEPSHORTCUT = 5;
+ SEARCHBOX = 6;
+ EDITTEXT = 7;
+ NOTIFICATION = 8;
+ TASK = 9;
+ }
+ enum Container {
+ DEFAULT_CONTAINER = 0;
+ HOTSEAT = 1;
+ FOLDER = 2;
+ PREDICTION = 3;
+ SEARCHRESULT = 4;
+ }
+ enum Control {
+ DEFAULT_CONTROL = 0;
+ MENU = 1;
+ UNINSTALL = 2;
+ REMOVE = 3;
+ }
+ optional Type type = 1;
+ optional Item item = 2;
+ optional Container container = 3;
+ optional Control control = 4;
+ optional string launch_component = 5;
+ optional int32 page_id = 6;
+ optional int32 grid_x = 7;
+ optional int32 grid_y = 8;
+}
+
+message LauncherExtension {
+ repeated LauncherTarget src_target = 1;
+ repeated LauncherTarget dst_target = 2;
+}
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index ebdcdfdd6c50..61174d99198b 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -47,7 +47,8 @@ AtomDecl::AtomDecl(const AtomDecl& that)
fields(that.fields),
primaryFields(that.primaryFields),
exclusiveField(that.exclusiveField),
- uidField(that.uidField) {}
+ uidField(that.uidField),
+ binaryFields(that.binaryFields) {}
AtomDecl::AtomDecl(int c, const string& n, const string& m)
:code(c),
@@ -116,6 +117,9 @@ java_type(const FieldDescriptor* field)
if (field->message_type()->full_name() ==
"android.os.statsd.AttributionNode") {
return JAVA_TYPE_ATTRIBUTION_CHAIN;
+ } else if (field->options().GetExtension(os::statsd::log_mode) ==
+ os::statsd::LogMode::MODE_BYTES) {
+ return JAVA_TYPE_BYTE_ARRAY;
} else {
return JAVA_TYPE_OBJECT;
}
@@ -185,6 +189,8 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
it != fields.end(); it++) {
const FieldDescriptor *field = it->second;
+ bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
+ os::statsd::LogMode::MODE_BYTES;
java_type_t javaType = java_type(field);
@@ -198,12 +204,19 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
field->name().c_str());
errorCount++;
continue;
- } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
+ } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
print_error(field, "Raw bytes type not allowed for field: %s\n",
field->name().c_str());
errorCount++;
continue;
}
+
+ if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
+ print_error(field, "Cannot mark field %s as bytes.\n",
+ field->name().c_str());
+ errorCount++;
+ continue;
+ }
}
// Check that if there's an attribution chain, it's at position 1.
@@ -228,12 +241,16 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
it != fields.end(); it++) {
const FieldDescriptor *field = it->second;
java_type_t javaType = java_type(field);
+ bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
+ os::statsd::LogMode::MODE_BYTES;
AtomField atField(field->name(), javaType);
if (javaType == JAVA_TYPE_ENUM) {
// All enums are treated as ints when it comes to function signatures.
signature->push_back(JAVA_TYPE_INT);
collate_enums(*field->enum_type(), &atField);
+ } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
+ signature->push_back(JAVA_TYPE_BYTE_ARRAY);
} else {
signature->push_back(javaType);
}
@@ -275,6 +292,10 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
errorCount++;
}
}
+ // Binary field validity is already checked above.
+ if (isBinaryField) {
+ atomDecl->binaryFields.push_back(it->first);
+ }
}
return errorCount;
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 5d2c30292c9c..a8b270caefaf 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -86,6 +86,8 @@ struct AtomDecl {
int uidField = 0;
+ vector<int> binaryFields;
+
AtomDecl();
AtomDecl(const AtomDecl& that);
AtomDecl(int code, const string& name, const string& message);
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index e519909aa026..485fc46387e4 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -68,6 +68,8 @@ cpp_type_name(java_type_t type)
return "double";
case JAVA_TYPE_STRING:
return "char const*";
+ case JAVA_TYPE_BYTE_ARRAY:
+ return "char const*";
default:
return "UNKNOWN";
}
@@ -90,6 +92,8 @@ java_type_name(java_type_t type)
return "double";
case JAVA_TYPE_STRING:
return "java.lang.String";
+ case JAVA_TYPE_BYTE_ARRAY:
+ return "byte[]";
default:
return "UNKNOWN";
}
@@ -200,13 +204,40 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
}
fprintf(out, " return options;\n");
- fprintf(out, " }\n");
+ fprintf(out, "}\n");
fprintf(out,
"const std::map<int, StateAtomFieldOptions> "
"AtomsInfo::kStateAtomsFieldOptions = "
"getStateAtomFieldOptions();\n");
+ fprintf(out,
+ "static std::map<int, std::vector<int>> "
+ "getBinaryFieldAtoms() {\n");
+ fprintf(out, " std::map<int, std::vector<int>> options;\n");
+ for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+ atom != atoms.decls.end(); atom++) {
+ if (atom->binaryFields.size() == 0) {
+ continue;
+ }
+ fprintf(out,
+ "\n // Adding binary fields for atom "
+ "(%d)%s\n",
+ atom->code, atom->name.c_str());
+
+ for (const auto& field : atom->binaryFields) {
+ fprintf(out, " options[static_cast<int>(%s)].push_back(%d);\n",
+ make_constant_name(atom->name).c_str(), field);
+ }
+ }
+
+ fprintf(out, " return options;\n");
+ fprintf(out, "}\n");
+
+ fprintf(out,
+ "const std::map<int, std::vector<int>> "
+ "AtomsInfo::kBytesFieldAtoms = "
+ "getBinaryFieldAtoms();\n");
fprintf(out, "int64_t lastRetryTimestampNs = -1;\n");
fprintf(out, "const int64_t kMinRetryIntervalNs = NS_PER_SEC * 60 * 20; // 20 minutes\n");
@@ -235,6 +266,9 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -277,6 +311,10 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
fprintf(out, " event.end();\n");
fprintf(out, " }\n");
fprintf(out, " event.end();\n\n");
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out,
+ " event.AppendCharArray(arg%d, arg%d_length);\n",
+ argIndex, argIndex);
} else {
if (*arg == JAVA_TYPE_STRING) {
fprintf(out, " if (arg%d == NULL) {\n", argIndex);
@@ -317,6 +355,9 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -343,6 +384,8 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", arg%d, arg%d_length", argIndex, argIndex);
} else {
fprintf(out, ", arg%d", argIndex);
}
@@ -491,6 +534,10 @@ static void write_cpp_usage(
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s %s, size_t %s_length",
+ cpp_type_name(field->javaType), field->name.c_str(),
+ field->name.c_str());
} else {
fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
}
@@ -518,6 +565,9 @@ static void write_cpp_method_header(
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s arg%d, size_t arg%d_length",
+ cpp_type_name(*arg), argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -600,6 +650,9 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio
fprintf(out,
" const static std::map<int, StateAtomFieldOptions> "
"kStateAtomsFieldOptions;\n");
+ fprintf(out,
+ " const static std::map<int, std::vector<int>> "
+ "kBytesFieldAtoms;");
fprintf(out, "};\n");
fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
@@ -632,6 +685,8 @@ static void write_java_usage(FILE* out, const string& method_name, const string&
field != atom.fields.end(); field++) {
if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
fprintf(out, ", android.os.WorkSource workSource");
+ } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", byte[] %s", field->name.c_str());
} else {
fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
}
@@ -821,6 +876,8 @@ jni_type_name(java_type_t type)
return "jdouble";
case JAVA_TYPE_STRING:
return "jstring";
+ case JAVA_TYPE_BYTE_ARRAY:
+ return "jbyteArray";
default:
return "UNKNOWN";
}
@@ -868,6 +925,9 @@ jni_function_name(const string& method_name, const vector<java_type_t>& signatur
case JAVA_TYPE_ATTRIBUTION_CHAIN:
result += "_AttributionChain";
break;
+ case JAVA_TYPE_BYTE_ARRAY:
+ result += "_bytes";
+ break;
default:
result += "_UNKNOWN";
break;
@@ -893,6 +953,8 @@ java_type_signature(java_type_t type)
return "D";
case JAVA_TYPE_STRING:
return "Ljava/lang/String;";
+ case JAVA_TYPE_BYTE_ARRAY:
+ return "[B";
default:
return "UNKNOWN";
}
@@ -960,6 +1022,32 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp
fprintf(out, " } else {\n");
fprintf(out, " str%d = NULL;\n", argIndex);
fprintf(out, " }\n");
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ hadStringOrChain = true;
+ fprintf(out, " jbyte* jbyte_array%d;\n", argIndex);
+ fprintf(out, " const char* str%d;\n", argIndex);
+ fprintf(out, " int str%d_length = 0;\n", argIndex);
+ fprintf(out,
+ " if (arg%d != NULL && env->GetArrayLength(arg%d) > "
+ "0) {\n",
+ argIndex, argIndex);
+ fprintf(out,
+ " jbyte_array%d = "
+ "env->GetByteArrayElements(arg%d, NULL);\n",
+ argIndex, argIndex);
+ fprintf(out,
+ " str%d_length = env->GetArrayLength(arg%d);\n",
+ argIndex, argIndex);
+ fprintf(out,
+ " str%d = "
+ "reinterpret_cast<char*>(env->GetByteArrayElements(arg%"
+ "d, NULL));\n",
+ argIndex, argIndex);
+ fprintf(out, " } else {\n");
+ fprintf(out, " jbyte_array%d = NULL;\n", argIndex);
+ fprintf(out, " str%d = NULL;\n", argIndex);
+ fprintf(out, " }\n");
+
} else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
hadStringOrChain = true;
for (auto chainField : attributionDecl.fields) {
@@ -1026,8 +1114,15 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp
}
}
} else {
- const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
+ const char* argName = (*arg == JAVA_TYPE_STRING ||
+ *arg == JAVA_TYPE_BYTE_ARRAY)
+ ? "str"
+ : "arg";
fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+
+ if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, ", %s%d_length", argName, argIndex);
+ }
}
argIndex++;
}
@@ -1043,6 +1138,13 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp
fprintf(out, " env->ReleaseStringUTFChars(arg%d, str%d);\n",
argIndex, argIndex);
fprintf(out, " }\n");
+ } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+ fprintf(out, " if (str%d != NULL) { \n", argIndex);
+ fprintf(out,
+ " env->ReleaseByteArrayElements(arg%d, "
+ "jbyte_array%d, 0);\n",
+ argIndex, argIndex);
+ fprintf(out, " }\n");
} else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
for (auto chainField : attributionDecl.fields) {
if (chainField.javaType == JAVA_TYPE_INT) {
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 264a865e3b39..188b765e241e 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -109,6 +109,28 @@ message BadAttributionNodePosition {
oneof event { BadAttributionNodePositionAtom bad = 1; }
}
+message GoodEventWithBinaryFieldAtom {
+ oneof event { GoodBinaryFieldAtom field1 = 1; }
+}
+
+message ComplexField {
+ optional string str = 1;
+}
+
+message GoodBinaryFieldAtom {
+ optional int32 field1 = 1;
+ optional ComplexField bf = 2 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+message BadEventWithBinaryFieldAtom {
+ oneof event { BadBinaryFieldAtom field1 = 1; }
+}
+
+message BadBinaryFieldAtom {
+ optional int32 field1 = 1;
+ optional ComplexField bf = 2;
+}
+
message BadStateAtoms {
oneof event {
BadStateAtom1 bad1 = 1;
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 1936d9667948..ad3bffacd442 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -212,5 +212,19 @@ TEST(CollationTest, PassOnGoodStateAtomOptions) {
EXPECT_EQ(0, errorCount);
}
+TEST(CollationTest, PassOnGoodBinaryFieldAtom) {
+ Atoms atoms;
+ int errorCount =
+ collate_atoms(GoodEventWithBinaryFieldAtom::descriptor(), &atoms);
+ EXPECT_EQ(0, errorCount);
+}
+
+TEST(CollationTest, FailOnBadBinaryFieldAtom) {
+ Atoms atoms;
+ int errorCount =
+ collate_atoms(BadEventWithBinaryFieldAtom::descriptor(), &atoms);
+ EXPECT_TRUE(errorCount > 0);
+}
+
} // namespace stats_log_api_gen
} // namespace android \ No newline at end of file