Merge "Add FrequencyRange comparator to ServiceState"
diff --git a/api/current.txt b/api/current.txt
index 7b48a5b..8f601cf 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -40855,7 +40855,9 @@
field public static final int SIOCGIFBRDADDR;
field public static final int SIOCGIFDSTADDR;
field public static final int SIOCGIFNETMASK;
+ field public static final int SOCK_CLOEXEC;
field public static final int SOCK_DGRAM;
+ field public static final int SOCK_NONBLOCK;
field public static final int SOCK_RAW;
field public static final int SOCK_SEQPACKET;
field public static final int SOCK_STREAM;
@@ -41934,9 +41936,12 @@
public static final class VideoProfile.CameraCapabilities implements android.os.Parcelable {
ctor public VideoProfile.CameraCapabilities(int, int);
+ ctor public VideoProfile.CameraCapabilities(int, int, boolean, float);
method public int describeContents();
method public int getHeight();
+ method public float getMaxZoom();
method public int getWidth();
+ method public boolean isZoomSupported();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telecom.VideoProfile.CameraCapabilities> CREATOR;
}
diff --git a/api/system-current.txt b/api/system-current.txt
index b7e3d68..5ef11a32 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5081,16 +5081,15 @@
public abstract class NetworkService extends android.app.Service {
ctor public NetworkService();
method protected abstract android.telephony.NetworkService.NetworkServiceProvider createNetworkServiceProvider(int);
- field public static final java.lang.String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
field public static final java.lang.String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
}
- public class NetworkService.NetworkServiceProvider {
+ public abstract class NetworkService.NetworkServiceProvider implements java.lang.AutoCloseable {
ctor public NetworkService.NetworkServiceProvider(int);
+ method public abstract void close();
method public void getNetworkRegistrationState(int, android.telephony.NetworkServiceCallback);
method public final int getSlotId();
method public final void notifyNetworkRegistrationStateChanged();
- method protected void onDestroy();
}
public class NetworkServiceCallback {
@@ -5427,20 +5426,19 @@
public abstract class DataService extends android.app.Service {
ctor public DataService();
method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
- field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
}
- public class DataService.DataServiceProvider {
+ public abstract class DataService.DataServiceProvider implements java.lang.AutoCloseable {
ctor public DataService.DataServiceProvider(int);
+ method public abstract void close();
method public void deactivateDataCall(int, int, android.telephony.data.DataServiceCallback);
method public void getDataCallList(android.telephony.data.DataServiceCallback);
method public final int getSlotId();
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
- method protected void onDestroy();
method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, int, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
@@ -5959,8 +5957,8 @@
field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
- field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
+ field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index a2a03b1..7dfe7d6 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -64,10 +64,22 @@
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 281f900..d9fa0f1 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -26,6 +26,7 @@
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 @@
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;
@@ -1166,6 +1168,14 @@
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 4e4f146..5d6d02b 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -379,7 +379,7 @@
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 a0ab3e4..11ce717 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 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 @@
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 @@
}
// 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 @@
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 2fcde29..b29de53 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 os {
namespace statsd {
+using std::string;
+using util::ProtoOutputStream;
+
TEST(LogEventTest, TestLogParsing) {
LogEvent event1(1, 2000);
@@ -159,6 +164,94 @@
}
+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/config/dirty-image-objects b/config/dirty-image-objects
index 9b4d199..9e2230b 100644
--- a/config/dirty-image-objects
+++ b/config/dirty-image-objects
@@ -44,7 +44,6 @@
sun.misc.FormattedFloatingDecimal
java.util.stream.IntStream
android.icu.util.TimeZone
-libcore.io.DropBox
org.apache.harmony.luni.internal.util.TimezoneGetter
dalvik.system.SocketTagger
dalvik.system.CloseGuard
@@ -137,7 +136,6 @@
android.icu.util.ULocale
dalvik.system.BaseDexClassLoader
android.icu.text.BreakIterator
-libcore.io.EventLogger
libcore.net.NetworkSecurityPolicy
android.icu.text.UnicodeSet
com.android.org.conscrypt.TrustedCertificateStore$PreloadHolder
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 495615b..fafcc6b 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6124,12 +6124,6 @@
libcore.io.ClassPathURLStreamHandler
libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection
libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection$1
-libcore.io.DropBox
-libcore.io.DropBox$DefaultReporter
-libcore.io.DropBox$Reporter
-libcore.io.EventLogger
-libcore.io.EventLogger$DefaultReporter
-libcore.io.EventLogger$Reporter
libcore.io.ForwardingOs
libcore.io.IoBridge
libcore.io.IoTracker
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 47fddfe..15af8a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -81,7 +81,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
-import android.os.DropBoxManager;
import android.os.Environment;
import android.os.GraphicsEnvironment;
import android.os.Handler;
@@ -157,8 +156,6 @@
import dalvik.system.VMDebug;
import dalvik.system.VMRuntime;
-import libcore.io.DropBox;
-import libcore.io.EventLogger;
import libcore.io.IoUtils;
import libcore.net.event.NetworkEventDispatcher;
@@ -6680,9 +6677,6 @@
}
}
- // add dropbox logging to libcore
- DropBox.setReporter(new DropBoxReporter());
-
ViewRootImpl.ConfigChangedCallback configChangedCallback
= (Configuration globalConfig) -> {
synchronized (mResourcesManager) {
@@ -6736,38 +6730,6 @@
}
}
- private static class EventLoggingReporter implements EventLogger.Reporter {
- @Override
- public void report (int code, Object... list) {
- EventLog.writeEvent(code, list);
- }
- }
-
- private class DropBoxReporter implements DropBox.Reporter {
-
- private DropBoxManager dropBox;
-
- public DropBoxReporter() {}
-
- @Override
- public void addData(String tag, byte[] data, int flags) {
- ensureInitialized();
- dropBox.addData(tag, data, flags);
- }
-
- @Override
- public void addText(String tag, String data) {
- ensureInitialized();
- dropBox.addText(tag, data);
- }
-
- private synchronized void ensureInitialized() {
- if (dropBox == null) {
- dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE);
- }
- }
- }
-
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
@@ -6778,9 +6740,6 @@
Environment.initForCurrentUser();
- // Set the reporter for event logging in libcore
- EventLogger.setReporter(new EventLoggingReporter());
-
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index d457a1b..a03e929 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -327,11 +327,13 @@
return false;
}
- if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
+ int dupFlags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
+ if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dupFlags)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed dup2(%d, %d) (%s): %s",
+ *error_msg = android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
fd,
new_fd,
+ dupFlags,
file_path.c_str(),
strerror(errno));
return false;
diff --git a/core/proto/android/stats/launcher/launcher.proto b/core/proto/android/stats/launcher/launcher.proto
new file mode 100644
index 0000000..dbd0e03
--- /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/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 906d64c..94879d0 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -61,18 +61,46 @@
case Instruction::Op::kInvokeDirect:
out << "kInvokeDirect";
return out;
+ case Instruction::Op::kInvokeStatic:
+ out << "kInvokeStatic";
+ return out;
+ case Instruction::Op::kInvokeInterface:
+ out << "kInvokeInterface";
+ return out;
case Instruction::Op::kBindLabel:
out << "kBindLabel";
return out;
case Instruction::Op::kBranchEqz:
out << "kBranchEqz";
return out;
+ case Instruction::Op::kBranchNEqz:
+ out << "kBranchNEqz";
+ return out;
case Instruction::Op::kNew:
out << "kNew";
return out;
}
}
+std::ostream& operator<<(std::ostream& out, const Value& value) {
+ if (value.is_register()) {
+ out << "Register(" << value.value() << ")";
+ } else if (value.is_parameter()) {
+ out << "Parameter(" << value.value() << ")";
+ } else if (value.is_immediate()) {
+ out << "Immediate(" << value.value() << ")";
+ } else if (value.is_string()) {
+ out << "String(" << value.value() << ")";
+ } else if (value.is_label()) {
+ out << "Label(" << value.value() << ")";
+ } else if (value.is_type()) {
+ out << "Type(" << value.value() << ")";
+ } else {
+ out << "UnknownValue";
+ }
+ return out;
+}
+
void* TrackingAllocator::Allocate(size_t size) {
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(size);
void* raw_buffer = buffer.get();
@@ -289,10 +317,16 @@
return EncodeInvoke(instruction, art::Instruction::INVOKE_VIRTUAL);
case Instruction::Op::kInvokeDirect:
return EncodeInvoke(instruction, art::Instruction::INVOKE_DIRECT);
+ case Instruction::Op::kInvokeStatic:
+ return EncodeInvoke(instruction, art::Instruction::INVOKE_STATIC);
+ case Instruction::Op::kInvokeInterface:
+ return EncodeInvoke(instruction, art::Instruction::INVOKE_INTERFACE);
case Instruction::Op::kBindLabel:
return BindLabel(instruction.args()[0]);
case Instruction::Op::kBranchEqz:
return EncodeBranch(art::Instruction::IF_EQZ, instruction);
+ case Instruction::Op::kBranchNEqz:
+ return EncodeBranch(art::Instruction::IF_NEZ, instruction);
case Instruction::Op::kNew:
return EncodeNew(instruction);
}
@@ -353,7 +387,9 @@
// If there is a return value, add a move-result instruction
if (instruction.dest().has_value()) {
- Encode11x(art::Instruction::MOVE_RESULT, RegisterValue(*instruction.dest()));
+ Encode11x(instruction.result_is_object() ? art::Instruction::MOVE_RESULT_OBJECT
+ : art::Instruction::MOVE_RESULT,
+ RegisterValue(*instruction.dest()));
}
max_args_ = std::max(max_args_, instruction.args().size());
@@ -447,7 +483,7 @@
auto& ir_node = dex_file_->methods_map[new_index];
SLICER_CHECK(ir_node == nullptr);
ir_node = decl;
- decl->orig_index = new_index;
+ decl->orig_index = decl->index = new_index;
entry = {id, decl};
}
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index adf82bf..45596ac 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -147,8 +147,11 @@
kMove,
kInvokeVirtual,
kInvokeDirect,
+ kInvokeStatic,
+ kInvokeInterface,
kBindLabel,
kBranchEqz,
+ kBranchNEqz,
kNew
};
@@ -163,19 +166,53 @@
// For most instructions, which take some number of arguments and have an optional return value.
template <typename... T>
static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
- return Instruction{opcode, /*method_id*/ 0, dest, args...};
+ return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
}
// For method calls.
template <typename... T>
static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
Value this_arg, T... args) {
- return Instruction{Op::kInvokeVirtual, method_id, dest, this_arg, args...};
+ return Instruction{
+ Op::kInvokeVirtual, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+ }
+ // Returns an object
+ template <typename... T>
+ static inline Instruction InvokeVirtualObject(size_t method_id, std::optional<const Value> dest,
+ Value this_arg, T... args) {
+ return Instruction{
+ Op::kInvokeVirtual, method_id, /*result_is_object=*/true, dest, this_arg, args...};
}
// For direct calls (basically, constructors).
template <typename... T>
static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
Value this_arg, T... args) {
- return Instruction{Op::kInvokeDirect, method_id, dest, this_arg, args...};
+ return Instruction{
+ Op::kInvokeDirect, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+ }
+ // Returns an object
+ template <typename... T>
+ static inline Instruction InvokeDirectObject(size_t method_id, std::optional<const Value> dest,
+ Value this_arg, T... args) {
+ return Instruction{
+ Op::kInvokeDirect, method_id, /*result_is_object=*/true, dest, this_arg, args...};
+ }
+ // For static calls.
+ template <typename... T>
+ static inline Instruction InvokeStatic(size_t method_id, std::optional<const Value> dest,
+ T... args) {
+ return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/false, dest, args...};
+ }
+ // Returns an object
+ template <typename... T>
+ static inline Instruction InvokeStaticObject(size_t method_id, std::optional<const Value> dest,
+ T... args) {
+ return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/true, dest, args...};
+ }
+ // For static calls.
+ template <typename... T>
+ static inline Instruction InvokeInterface(size_t method_id, std::optional<const Value> dest,
+ T... args) {
+ return Instruction{Op::kInvokeInterface, method_id, /*result_is_object=*/false, dest, args...};
}
///////////////
@@ -184,21 +221,27 @@
Op opcode() const { return opcode_; }
size_t method_id() const { return method_id_; }
+ bool result_is_object() const { return result_is_object_; }
const std::optional<const Value>& dest() const { return dest_; }
const std::vector<const Value>& args() const { return args_; }
private:
inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest)
- : opcode_{opcode}, method_id_{method_id}, dest_{dest}, args_{} {}
+ : opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {}
template <typename... T>
- inline constexpr Instruction(Op opcode, size_t method_id, std::optional<const Value> dest,
- T... args)
- : opcode_{opcode}, method_id_{method_id}, dest_{dest}, args_{args...} {}
+ inline constexpr Instruction(Op opcode, size_t method_id, bool result_is_object,
+ std::optional<const Value> dest, T... args)
+ : opcode_{opcode},
+ method_id_{method_id},
+ result_is_object_{result_is_object},
+ dest_{dest},
+ args_{args...} {}
const Op opcode_;
// The index of the method to invoke, for kInvokeVirtual and similar opcodes.
const size_t method_id_{0};
+ const bool result_is_object_;
const std::optional<const Value> dest_;
const std::vector<const Value> args_;
};
@@ -244,6 +287,8 @@
// TODO: add builders for more instructions
+ DexBuilder* dex_file() const { return dex_; }
+
private:
void EncodeInstructions();
void EncodeInstruction(const Instruction& instruction);
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index e20f3a9..1508ad9e 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -84,6 +84,15 @@
}
@Test
+ public void returnIfNotZero() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("returnIfNotZero", int.class);
+ Assert.assertEquals(3, method.invoke(null, 0));
+ Assert.assertEquals(5, method.invoke(null, 17));
+ }
+
+ @Test
public void backwardsBranch() throws Exception {
ClassLoader loader = loadDexFile("simple.dex");
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
@@ -124,4 +133,22 @@
Assert.assertEquals("b", method.invoke(null, 0));
Assert.assertEquals("a", method.invoke(null, 1));
}
+
+ @Test
+ public void invokeStaticReturnObject() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("invokeStaticReturnObject", int.class, int.class);
+ Assert.assertEquals("10", method.invoke(null, 10, 10));
+ Assert.assertEquals("a", method.invoke(null, 10, 16));
+ Assert.assertEquals("5", method.invoke(null, 5, 16));
+ }
+
+ @Test
+ public void invokeVirtualReturnObject() throws Exception {
+ ClassLoader loader = loadDexFile("simple.dex");
+ Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+ Method method = clazz.getMethod("invokeVirtualReturnObject", String.class, int.class);
+ Assert.assertEquals("bc", method.invoke(null, "abc", 1));
+ }
}
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index e2bf43bc..2781aa5 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -108,6 +108,27 @@
}
returnIfZero.Encode();
+ // int returnIfNotZero(int x) { if (x != 0) { return 5; } else { return 3; } }
+ MethodBuilder returnIfNotZero{cbuilder.CreateMethod(
+ "returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
+ {
+ Value resultIfNotZero{returnIfNotZero.MakeRegister()};
+ Value else_target{returnIfNotZero.MakeLabel()};
+ returnIfNotZero.AddInstruction(Instruction::OpWithArgs(
+ Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target));
+ // else branch
+ returnIfNotZero.BuildConst4(resultIfNotZero, 3);
+ returnIfNotZero.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero));
+ // then branch
+ returnIfNotZero.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, else_target));
+ returnIfNotZero.BuildConst4(resultIfNotZero, 5);
+ returnIfNotZero.AddInstruction(
+ Instruction::OpWithArgs(Instruction::Op::kReturn, /*dest=*/{}, resultIfNotZero));
+ }
+ returnIfNotZero.Encode();
+
// Make sure backwards branches work too.
//
// Pseudo code for test:
@@ -216,6 +237,38 @@
method.Encode();
}(returnStringIfZeroBA);
+ // Make sure we can invoke static methods that return an object
+ // String invokeStaticReturnObject(int n, int radix) { return java.lang.Integer.toString(n,
+ // radix); }
+ MethodBuilder invokeStaticReturnObject{
+ cbuilder.CreateMethod("invokeStaticReturnObject",
+ Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
+ [&](MethodBuilder& method) {
+ Value result{method.MakeRegister()};
+ MethodDeclData to_string{dex_file.GetOrDeclareMethod(
+ TypeDescriptor::FromClassname("java.lang.Integer"),
+ "toString",
+ Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
+ method.AddInstruction(Instruction::InvokeStaticObject(
+ to_string.id, result, Value::Parameter(0), Value::Parameter(1)));
+ method.BuildReturn(result, /*is_object=*/true);
+ method.Encode();
+ }(invokeStaticReturnObject);
+
+ // Make sure we can invoke virtual methods that return an object
+ // String invokeVirtualReturnObject(String s, int n) { return s.substring(n); }
+ MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod(
+ "invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})};
+ [&](MethodBuilder& method) {
+ Value result{method.MakeRegister()};
+ MethodDeclData substring{dex_file.GetOrDeclareMethod(
+ string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})};
+ method.AddInstruction(Instruction::InvokeVirtualObject(
+ substring.id, result, Value::Parameter(0), Value::Parameter(1)));
+ method.BuildReturn(result, /*is_object=*/true);
+ method.Encode();
+ }(invokeVirtualReturnObject);
+
slicer::MemView image{dex_file.CreateImage()};
std::ofstream out_file(outdir + "/simple.dex");
out_file.write(image.ptr<const char>(), image.size());
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index bbac8eb..7b23061 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -369,16 +369,13 @@
}
/**
- * Create a call camera capabilities instance that optionally
- * supports zoom.
+ * Create a call camera capabilities instance that optionally supports zoom.
*
* @param width The width of the camera video (in pixels).
* @param height The height of the camera video (in pixels).
* @param zoomSupported True when camera supports zoom.
* @param maxZoom Maximum zoom supported by camera.
- * @hide
*/
- @UnsupportedAppUsage
public CameraCapabilities(int width, int height, boolean zoomSupported, float maxZoom) {
mWidth = width;
mHeight = height;
@@ -455,16 +452,14 @@
}
/**
- * Whether the camera supports zoom.
- * @hide
+ * Returns {@code true} is zoom is supported, {@code false} otherwise.
*/
public boolean isZoomSupported() {
return mZoomSupported;
}
/**
- * The maximum zoom supported by the camera.
- * @hide
+ * Returns the maximum zoom supported by the camera.
*/
public float getMaxZoom() {
return mMaxZoom;
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 4354314..4bca404 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -16,7 +16,6 @@
package android.telephony;
-import android.annotation.CallSuper;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -53,7 +52,6 @@
private final String TAG = NetworkService.class.getSimpleName();
public static final String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
- public static final String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
private static final int NETWORK_SERVICE_CREATE_NETWORK_SERVICE_PROVIDER = 1;
private static final int NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER = 2;
@@ -81,7 +79,7 @@
* must extend this class to support network connection. Note that each instance of network
* service is associated with one physical SIM slot.
*/
- public class NetworkServiceProvider {
+ public abstract class NetworkServiceProvider implements AutoCloseable {
private final int mSlotId;
private final List<INetworkServiceCallback>
@@ -137,12 +135,12 @@
}
/**
- * Called when the instance of network service is destroyed (e.g. got unbind or binder died).
+ * Called when the instance of network service is destroyed (e.g. got unbind or binder died)
+ * or when the network service provider is removed. The extended class should implement this
+ * method to perform cleanup works.
*/
- @CallSuper
- protected void onDestroy() {
- mNetworkRegistrationStateChangedCallbacks.clear();
- }
+ @Override
+ public abstract void close();
}
private class NetworkServiceHandler extends Handler {
@@ -168,7 +166,7 @@
case NETWORK_SERVICE_REMOVE_NETWORK_SERVICE_PROVIDER:
// If the service provider doesn't exist yet, we try to create it.
if (serviceProvider != null) {
- serviceProvider.onDestroy();
+ serviceProvider.close();
mServiceMap.remove(slotId);
}
break;
@@ -176,7 +174,7 @@
for (int i = 0; i < mServiceMap.size(); i++) {
serviceProvider = mServiceMap.get(i);
if (serviceProvider != null) {
- serviceProvider.onDestroy();
+ serviceProvider.close();
}
}
mServiceMap.clear();
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 1db5850..74d1e83 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -16,7 +16,6 @@
package android.telephony.data;
-import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,7 +59,6 @@
private static final String TAG = DataService.class.getSimpleName();
public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
- public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
/** {@hide} */
@IntDef(prefix = "REQUEST_REASON_", value = {
@@ -116,7 +114,7 @@
* must extend this class to support data connection. Note that each instance of data service
* provider is associated with one physical SIM slot.
*/
- public class DataServiceProvider {
+ public abstract class DataServiceProvider implements AutoCloseable {
private final int mSlotId;
@@ -250,12 +248,12 @@
}
/**
- * Called when the instance of data service is destroyed (e.g. got unbind or binder died).
+ * Called when the instance of data service is destroyed (e.g. got unbind or binder died)
+ * or when the data service provider is removed. The extended class should implement this
+ * method to perform cleanup works.
*/
- @CallSuper
- protected void onDestroy() {
- mDataCallListChangedCallbacks.clear();
- }
+ @Override
+ public abstract void close();
}
private static final class SetupDataCallRequest {
@@ -345,7 +343,7 @@
break;
case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER:
if (serviceProvider != null) {
- serviceProvider.onDestroy();
+ serviceProvider.close();
mServiceMap.remove(slotId);
}
break;
@@ -353,7 +351,7 @@
for (int i = 0; i < mServiceMap.size(); i++) {
serviceProvider = mServiceMap.get(i);
if (serviceProvider != null) {
- serviceProvider.onDestroy();
+ serviceProvider.close();
}
}
mServiceMap.clear();
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 57d9cce..45b4849 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -151,7 +151,7 @@
/**
* Called when the qualified networks updater is removed. The extended class should
- * implement this method to perform clean up works.
+ * implement this method to perform cleanup works.
*/
@Override
public abstract void close();
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index ebdcdfd..61174d9 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -47,7 +47,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 5d2c302..a8b270c 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -86,6 +86,8 @@
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 e519909..485fc46 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -68,6 +68,8 @@
return "double";
case JAVA_TYPE_STRING:
return "char const*";
+ case JAVA_TYPE_BYTE_ARRAY:
+ return "char const*";
default:
return "UNKNOWN";
}
@@ -90,6 +92,8 @@
return "double";
case JAVA_TYPE_STRING:
return "java.lang.String";
+ case JAVA_TYPE_BYTE_ARRAY:
+ return "byte[]";
default:
return "UNKNOWN";
}
@@ -200,13 +204,40 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
return "jdouble";
case JAVA_TYPE_STRING:
return "jstring";
+ case JAVA_TYPE_BYTE_ARRAY:
+ return "jbyteArray";
default:
return "UNKNOWN";
}
@@ -868,6 +925,9 @@
case JAVA_TYPE_ATTRIBUTION_CHAIN:
result += "_AttributionChain";
break;
+ case JAVA_TYPE_BYTE_ARRAY:
+ result += "_bytes";
+ break;
default:
result += "_UNKNOWN";
break;
@@ -893,6 +953,8 @@
return "D";
case JAVA_TYPE_STRING:
return "Ljava/lang/String;";
+ case JAVA_TYPE_BYTE_ARRAY:
+ return "[B";
default:
return "UNKNOWN";
}
@@ -960,6 +1022,32 @@
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 @@
}
}
} 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 @@
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 264a865..188b765 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -109,6 +109,28 @@
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 1936d96..ad3bffac 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -212,5 +212,19 @@
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