diff options
25 files changed, 607 insertions, 111 deletions
diff --git a/api/current.txt b/api/current.txt index 7b48a5b60dc3..8f601cfc8ebc 100755 --- a/api/current.txt +++ b/api/current.txt @@ -40855,7 +40855,9 @@ package android.system { 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 @@ package android.telecom { 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 b7e3d685497d..5ef11a32b12f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5081,16 +5081,15 @@ package android.telephony { 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 @@ package android.telephony.data { 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 @@ package android.telephony.ims { 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 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 281f900d9cb9..d9fa0f13f5bf 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; @@ -1166,6 +1168,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/config/dirty-image-objects b/config/dirty-image-objects index 9b4d199dc723..9e2230b288c8 100644 --- a/config/dirty-image-objects +++ b/config/dirty-image-objects @@ -44,7 +44,6 @@ java.util.function.ToIntFunction 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 @@ java.lang.CharSequence 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 495615b90b15..fafcc6bea793 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -6124,12 +6124,6 @@ libcore.io.BufferIterator 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 47fddfe80e07..15af8a90dd49 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -81,7 +81,6 @@ import android.os.Binder; 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.CloseGuard; 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 @@ public final class ActivityThread extends ClientTransactionHandler { } } - // add dropbox logging to libcore - DropBox.setReporter(new DropBoxReporter()); - ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> { synchronized (mResourcesManager) { @@ -6736,38 +6730,6 @@ public final class ActivityThread extends ClientTransactionHandler { } } - 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 @@ public final class ActivityThread extends ClientTransactionHandler { 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 d457a1b48e38..a03e929fa60b 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -327,11 +327,13 @@ bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const { 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 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/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc index 906d64c1f619..94879d0bbcc4 100644 --- a/startop/view_compiler/dex_builder.cc +++ b/startop/view_compiler/dex_builder.cc @@ -61,18 +61,46 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) { 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 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) { 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 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct // 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 @@ const MethodDeclData& DexBuilder::GetOrDeclareMethod(TypeDescriptor type, const 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 adf82bf9a01a..45596acfdc44 100644 --- a/startop/view_compiler/dex_builder.h +++ b/startop/view_compiler/dex_builder.h @@ -147,8 +147,11 @@ class Instruction { kMove, kInvokeVirtual, kInvokeDirect, + kInvokeStatic, + kInvokeInterface, kBindLabel, kBranchEqz, + kBranchNEqz, kNew }; @@ -163,19 +166,53 @@ class Instruction { // 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 @@ class Instruction { 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 @@ class MethodBuilder { // 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 e20f3a9406c0..1508ad9ee56b 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 @@ public class DexBuilderTest { } @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 @@ public class DexBuilderTest { 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 e2bf43bc1d0c..2781aa55d1df 100644 --- a/startop/view_compiler/dex_testcase_generator.cc +++ b/startop/view_compiler/dex_testcase_generator.cc @@ -108,6 +108,27 @@ void GenerateSimpleTestCases(const string& outdir) { } 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 @@ void GenerateSimpleTestCases(const string& outdir) { 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 bbac8eb88aec..7b2306128b7b 100644 --- a/telecomm/java/android/telecom/VideoProfile.java +++ b/telecomm/java/android/telecom/VideoProfile.java @@ -369,16 +369,13 @@ public class VideoProfile implements Parcelable { } /** - * 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 @@ public class VideoProfile implements Parcelable { } /** - * 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 4354314dac29..4bca404d9444 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 @@ public abstract class NetworkService extends Service { 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 @@ public abstract class NetworkService extends Service { * 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 @@ public abstract class NetworkService extends Service { } /** - * 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 @@ public abstract class NetworkService extends Service { 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 @@ public abstract class NetworkService extends Service { 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 1db58506bb1c..74d1e838f186 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 @@ public abstract class DataService extends Service { 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 @@ public abstract class DataService extends Service { * 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 @@ public abstract class DataService extends Service { } /** - * 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 @@ public abstract class DataService extends Service { break; case DATA_SERVICE_REMOVE_DATA_SERVICE_PROVIDER: if (serviceProvider != null) { - serviceProvider.onDestroy(); + serviceProvider.close(); mServiceMap.remove(slotId); } break; @@ -353,7 +351,7 @@ public abstract class DataService extends Service { 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 57d9cced53b1..45b4849990c2 100644 --- a/telephony/java/android/telephony/data/QualifiedNetworksService.java +++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java @@ -151,7 +151,7 @@ public abstract class QualifiedNetworksService extends Service { /** * 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 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 |