diff options
132 files changed, 3969 insertions, 1014 deletions
diff --git a/Android.bp b/Android.bp index 69ee848cadad..03a79f61def7 100644 --- a/Android.bp +++ b/Android.bp @@ -730,10 +730,18 @@ gensrcs { "core/proto/android/os/procrank.proto", "core/proto/android/os/ps.proto", "core/proto/android/os/system_properties.proto", + "core/proto/android/util/event_log_tags.proto", ], // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool - cmd: "PATH=$$PATH:$$(dirname $(location protoc-gen-cppstream)) $(location aprotoc) --plugin=protoc-gen-cpp-stream=$(location protoc-gen-cppstream) --dependency_out=$(depfile) --cppstream_out=$(genDir)/ -Iexternal/protobuf/src -I . $(in)", + cmd: "mkdir -p $(genDir) " + + "&& $(location aprotoc) " + + " --plugin=$(location protoc-gen-cppstream) " + + " --dependency_out=$(depfile) " + + " --cppstream_out=$(genDir) " + + " -Iexternal/protobuf/src " + + " -I . " + + " $(in)", output_extension = "proto.h", } diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index e23e80ae21e8..847b26a39ffe 100644 --- a/cmds/incident_helper/src/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -208,6 +208,19 @@ bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) { return true; } +std::string behead(std::string* line, const char cut) { + auto found = line->find_first_of(cut); + if (found == std::string::npos) { + std::string head = line->substr(0); + line->assign(""); + return head; + } + std::string head = line->substr(0, found); + while(line->at(found) == cut) found++; // trim more cut of the rest + line->assign(line->substr(found)); + return head; +} + int toInt(const std::string& s) { return atoi(s.c_str()); } diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h index b063b2fe0bba..53f443873e4d 100644 --- a/cmds/incident_helper/src/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -34,6 +34,8 @@ const std::string DEFAULT_WHITESPACE = " \t"; const std::string DEFAULT_NEWLINE = "\r\n"; const std::string TAB_DELIMITER = "\t"; const std::string COMMA_DELIMITER = ","; +const std::string PIPE_DELIMITER = "|"; +const std::string PARENTHESES_DELIMITER = "()"; // returns true if c is a-zA-Z0-9 or underscore bool isValidChar(char c); @@ -89,6 +91,11 @@ bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false); /** + * behead the given line by the cut, return the head and reassign the line to be the rest. + */ +std::string behead(std::string* line, const char cut); + +/** * Converts string to the desired type */ int toInt(const std::string& s); diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp index 8c6cd78d3bf2..418dc3fad761 100644 --- a/cmds/incident_helper/src/main.cpp +++ b/cmds/incident_helper/src/main.cpp @@ -19,6 +19,7 @@ #include "parsers/BatteryTypeParser.h" #include "parsers/CpuFreqParser.h" #include "parsers/CpuInfoParser.h" +#include "parsers/EventLogTagsParser.h" #include "parsers/KernelWakesParser.h" #include "parsers/PageTypeInfoParser.h" #include "parsers/ProcrankParser.h" @@ -55,6 +56,8 @@ static TextParserBase* selectParser(int section) { // IDs larger than 1 are section ids reserved in incident.proto case 1000: return new SystemPropertiesParser(); + case 1100: + return new EventLogTagsParser(); case 2000: return new ProcrankParser(); case 2001: diff --git a/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp b/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp new file mode 100644 index 000000000000..73e37bd166cd --- /dev/null +++ b/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/util/event_log_tags.proto.h" +#include "ih_util.h" +#include "EventLogTagsParser.h" + +status_t +EventLogTagsParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + + ProtoOutputStream proto; + + // parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + string debug = line; + string tagNumber = behead(&line, ' '); + string tagName = behead(&line, ' '); + if (tagNumber == "" || tagName == "") { + fprintf(stderr, "Bad line, expect at least two parts: %s[%s, %s]\n", + debug.c_str(), tagNumber.c_str(), tagName.c_str()); + continue; + } + + long long token = proto.start(EventLogTagMapProto::EVENT_LOG_TAGS); + proto.write(EventLogTag::TAG_NUMBER, toInt(tagNumber)); + proto.write(EventLogTag::TAG_NAME, tagName); + + record_t valueDescriptors = parseRecord(line, PARENTHESES_DELIMITER); + for (size_t i = 0; i < valueDescriptors.size(); i++) { + record_t valueDescriptor = parseRecord(valueDescriptors[i], PIPE_DELIMITER); + if (valueDescriptor.size() != 2 && valueDescriptor.size() != 3) { + // If the parts doesn't contains pipe, then skips it. + continue; + } + long long descriptorToken = proto.start(EventLogTag::VALUE_DESCRIPTORS); + proto.write(EventLogTag::ValueDescriptor::NAME, valueDescriptor[0]); + proto.write(EventLogTag::ValueDescriptor::TYPE, toInt(valueDescriptor[1])); + if (valueDescriptor.size() == 3) { + char c = valueDescriptor[2][0]; + int unit = 0; + if (c < '0' || c > '9') { + unit = (int) c; + } else { + unit = toInt(valueDescriptor[2]); + } + proto.write(EventLogTag::ValueDescriptor::UNIT, unit); + } + proto.end(descriptorToken); + } + proto.end(token); + } + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +} diff --git a/cmds/incident_helper/src/parsers/EventLogTagsParser.h b/cmds/incident_helper/src/parsers/EventLogTagsParser.h new file mode 100644 index 000000000000..79057ce0b3ca --- /dev/null +++ b/cmds/incident_helper/src/parsers/EventLogTagsParser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_LOG_TAGS_PARSER_H +#define EVENT_LOG_TAGS_PARSER_H + +#include "TextParserBase.h" + +using namespace android; + +/** + * event.logtags parser, parse file in /system/etc/event-log-tags + */ +class EventLogTagsParser : public TextParserBase { +public: + EventLogTagsParser() : TextParserBase(String8("EventLogTagsParser")) {}; + ~EventLogTagsParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // EVENT_LOG_TAGS_PARSER_H diff --git a/cmds/incident_helper/testdata/event-log-tags.txt b/cmds/incident_helper/testdata/event-log-tags.txt new file mode 100644 index 000000000000..35396bfb4250 --- /dev/null +++ b/cmds/incident_helper/testdata/event-log-tags.txt @@ -0,0 +1,6 @@ +42 answer (to life the universe etc|3) +314 pi +1004 chatty (dropped|3) +1005 tag_def (tag|1),(name|3),(format|3) +2747 contacts_aggregation (aggregation time|2|3), (count|1|1) +1397638484 snet_event_log (subtag|3) (uid|1) (message|3|s)
\ No newline at end of file diff --git a/cmds/incident_helper/tests/EventLogTagsParser_test.cpp b/cmds/incident_helper/tests/EventLogTagsParser_test.cpp new file mode 100644 index 000000000000..d0d1f1e023a8 --- /dev/null +++ b/cmds/incident_helper/tests/EventLogTagsParser_test.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "EventLogTagsParser.h" + +#include "frameworks/base/core/proto/android/util/event_log_tags.pb.h" + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gmock/gmock.h> +#include <google/protobuf/message_lite.h> +#include <gtest/gtest.h> +#include <string.h> +#include <fcntl.h> + +using namespace android::base; +using namespace android::util; +using namespace std; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStderr; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStderr; +using ::testing::internal::GetCapturedStdout; + +class EventLogTagsParserTest : public Test { +public: + virtual void SetUp() override { + ASSERT_TRUE(tf.fd != -1); + } + +protected: + TemporaryFile tf; + + const string kTestPath = GetExecutableDirectory(); + const string kTestDataPath = kTestPath + "/testdata/"; +}; + +TEST_F(EventLogTagsParserTest, Success) { + const string testFile = kTestDataPath + "event-log-tags.txt"; + + EventLogTagsParser parser; + EventLogTagMapProto expected; + + EventLogTag* eventLogTag; + EventLogTag::ValueDescriptor* desp; + + eventLogTag = expected.add_event_log_tags(); + eventLogTag->set_tag_number(42); + eventLogTag->set_tag_name("answer"); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("to life the universe etc"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING); + + eventLogTag = expected.add_event_log_tags(); + eventLogTag->set_tag_number(314); + eventLogTag->set_tag_name("pi"); + + eventLogTag = expected.add_event_log_tags(); + eventLogTag->set_tag_number(1004); + eventLogTag->set_tag_name("chatty"); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("dropped"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING); + + eventLogTag = expected.add_event_log_tags(); + eventLogTag->set_tag_number(1005); + eventLogTag->set_tag_name("tag_def"); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("tag"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_INT); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("name"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("format"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING); + + eventLogTag = expected.add_event_log_tags(); + eventLogTag->set_tag_number(2747); + eventLogTag->set_tag_name("contacts_aggregation"); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("aggregation time"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_LONG); + desp->set_unit(EventLogTag_ValueDescriptor_DataUnit_MILLISECONDS); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("count"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_INT); + desp->set_unit(EventLogTag_ValueDescriptor_DataUnit_OBJECTS); + + eventLogTag = expected.add_event_log_tags(); + eventLogTag->set_tag_number(1397638484); + eventLogTag->set_tag_name("snet_event_log"); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("subtag"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("uid"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_INT); + desp = eventLogTag->add_value_descriptors(); + desp->set_name("message"); + desp->set_type(EventLogTag_ValueDescriptor_DataType_STRING); + desp->set_unit(EventLogTag_ValueDescriptor_DataUnit_SECONDS); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString()); + close(fd); +} diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp index 7b8cf52c8bee..efe714d98b18 100644 --- a/cmds/incident_helper/tests/ih_util_test.cpp +++ b/cmds/incident_helper/tests/ih_util_test.cpp @@ -60,6 +60,9 @@ TEST(IhUtilTest, ParseRecord) { result = parseRecord("123,456,78_9", ","); expected = { "123", "456", "78_9" }; EXPECT_EQ(expected, result); + + result = parseRecord("", " "); + EXPECT_TRUE(result.empty()); } TEST(IhUtilTest, ParseRecordByColumns) { @@ -133,6 +136,22 @@ TEST(IhUtilTest, stripSuffix) { EXPECT_THAT(data4, StrEq(" 243%abc")); } +TEST(IhUtilTest, behead) { + string testcase1 = "81002 dropbox_file_copy (a)(b)"; + EXPECT_THAT(behead(&testcase1, ' '), StrEq("81002")); + EXPECT_THAT(behead(&testcase1, ' '), StrEq("dropbox_file_copy")); + EXPECT_THAT(testcase1, "(a)(b)"); + + string testcase2 = "adbce,erwqr"; + EXPECT_THAT(behead(&testcase2, ' '), StrEq("adbce,erwqr")); + EXPECT_THAT(testcase2, ""); + + string testcase3 = "first second"; + EXPECT_THAT(behead(&testcase3, ' '), StrEq("first")); + EXPECT_THAT(behead(&testcase3, ' '), StrEq("second")); + EXPECT_THAT(testcase3, ""); +} + TEST(IhUtilTest, Reader) { TemporaryFile tf; ASSERT_NE(tf.fd, -1); diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk index 11d3e4911761..8420bc8f7ac7 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -14,6 +14,9 @@ LOCAL_PATH:= $(call my-dir) +# proto files used in incidentd to generate cppstream proto headers. +PROTO_FILES:= frameworks/base/core/proto/android/util/log.proto + # ========= # # incidentd # # ========= # @@ -59,20 +62,38 @@ LOCAL_SHARED_LIBRARIES := \ libutils LOCAL_MODULE_CLASS := EXECUTABLES + gen_src_dir := $(local-generated-sources-dir) -GEN := $(gen_src_dir)/src/section_list.cpp -$(GEN): $(HOST_OUT_EXECUTABLES)/incident-section-gen -$(GEN): PRIVATE_CUSTOM_TOOL = \ +# generate section_list.cpp +GEN_LIST := $(gen_src_dir)/src/section_list.cpp +$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen +$(GEN_LIST): PRIVATE_CUSTOM_TOOL = \ $(HOST_OUT_EXECUTABLES)/incident-section-gen incidentd > $@ -$(GEN): $(HOST_OUT_EXECUTABLES)/incident-section-gen +$(GEN_LIST): $(HOST_OUT_EXECUTABLES)/incident-section-gen + $(transform-generated-source) +LOCAL_GENERATED_SOURCES += $(GEN_LIST) +GEN_LIST:= + +# generate cppstream proto, add proto files to PROTO_FILES +GEN_PROTO := $(gen_src_dir)/proto.timestamp +$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES) +$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir) +$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \ + $(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \ + --cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \ + $(PROTO_FILES) \ + && touch $@ +$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(transform-generated-source) -LOCAL_GENERATED_SOURCES += $(GEN) +LOCAL_GENERATED_SOURCES += $(GEN_PROTO) +GEN_PROTO:= gen_src_dir:= -GEN:= +ifeq ($(BUILD_WITH_INCIDENTD_RC), true) LOCAL_INIT_RC := incidentd.rc +endif include $(BUILD_EXECUTABLE) @@ -120,4 +141,22 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, testdata) +LOCAL_MODULE_CLASS := NATIVE_TESTS +gen_src_dir := $(local-generated-sources-dir) +# generate cppstream proto for testing +GEN_PROTO := $(gen_src_dir)/log.proto.timestamp +$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc $(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream $(PROTO_FILES) +$(GEN_PROTO): PRIVATE_GEN_SRC_DIR := $(gen_src_dir) +$(GEN_PROTO): PRIVATE_CUSTOM_TOOL = \ + $(HOST_OUT_EXECUTABLES)/aprotoc --plugin=protoc-gen-cppstream=$(HOST_OUT_EXECUTABLES)/protoc-gen-cppstream \ + --cppstream_out=$(PRIVATE_GEN_SRC_DIR) -Iexternal/protobuf/src -I . \ + $(PROTO_FILES) \ + && touch $@ +$(GEN_PROTO): $(HOST_OUT_EXECUTABLES)/aprotoc + $(transform-generated-source) +LOCAL_GENERATED_SOURCES += $(GEN_PROTO) +GEN_PROTO:= + +gen_src_dir:= + include $(BUILD_NATIVE_TEST) diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 22053ef3c53a..61d16f815e65 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -16,21 +16,29 @@ #define LOG_TAG "incidentd" -#include "FdBuffer.h" -#include "Privacy.h" -#include "PrivacyBuffer.h" #include "Section.h" -#include "io_util.h" -#include "section_list.h" +#include <errno.h> +#include <unistd.h> +#include <wait.h> + +#include <memory> +#include <mutex> #include <android/util/protobuf.h> -#include <private/android_filesystem_config.h> #include <binder/IServiceManager.h> -#include <map> -#include <mutex> -#include <wait.h> -#include <unistd.h> +#include <log/log_event_list.h> +#include <log/logprint.h> +#include <log/log_read.h> +#include <private/android_filesystem_config.h> // for AID_NOBODY +#include <private/android_logger.h> + +#include "FdBuffer.h" +#include "frameworks/base/core/proto/android/util/log.proto.h" +#include "io_util.h" +#include "Privacy.h" +#include "PrivacyBuffer.h" +#include "section_list.h" using namespace android::util; using namespace std; @@ -41,7 +49,7 @@ const int FIELD_ID_INCIDENT_HEADER = 1; // incident section parameters const int WAIT_MAX = 5; const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000}; -const char* INCIDENT_HELPER = "/system/bin/incident_helper"; +const char INCIDENT_HELPER[] = "/system/bin/incident_helper"; static pid_t fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe) @@ -609,3 +617,160 @@ DumpsysSection::BlockingCall(int pipeWriteFd) const return NO_ERROR; } + +// ================================================================================ +// initialization only once in Section.cpp. +map<log_id_t, log_time> LogSection::gLastLogsRetrieved; + +LogSection::LogSection(int id, log_id_t logID) + :WorkerThreadSection(id), + mLogID(logID) +{ + name += "logcat "; + name += android_log_id_to_name(logID); + switch (logID) { + case LOG_ID_EVENTS: + case LOG_ID_STATS: + case LOG_ID_SECURITY: + mBinary = true; + break; + default: + mBinary = false; + } +} + +LogSection::~LogSection() +{ +} + +static size_t +trimTail(char const* buf, size_t len) +{ + while (len > 0) { + char c = buf[len - 1]; + if (c == '\0' || c == ' ' || c == '\n' || c == '\r' || c == ':') { + len--; + } else { + break; + } + } + return len; +} + +static inline int32_t get4LE(uint8_t const* src) { + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +} + +status_t +LogSection::BlockingCall(int pipeWriteFd) const +{ + status_t err = NO_ERROR; + // Open log buffer and getting logs since last retrieved time if any. + unique_ptr<logger_list, void (*)(logger_list*)> loggers( + gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end() ? + android_logger_list_alloc(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) : + android_logger_list_alloc_time(ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, + gLastLogsRetrieved[mLogID], 0), + android_logger_list_free); + + if (android_logger_open(loggers.get(), mLogID) == NULL) { + ALOGW("LogSection %s: Can't get logger.", this->name.string()); + return err; + } + + log_msg msg; + log_time lastTimestamp(0); + + ProtoOutputStream proto; + while (true) { // keeps reading until logd buffer is fully read. + status_t err = android_logger_list_read(loggers.get(), &msg); + // err = 0 - no content, unexpected connection drop or EOF. + // err = +ive number - size of retrieved data from logger + // err = -ive number, OS supplied error _except_ for -EAGAIN + // err = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end of data. + if (err <= 0) { + if (err != -EAGAIN) { + ALOGE("LogSection %s: fails to read a log_msg.\n", this->name.string()); + } + break; + } + if (mBinary) { + // remove the first uint32 which is tag's index in event log tags + android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t), + msg.len() - sizeof(uint32_t));; + android_log_list_element elem; + + lastTimestamp.tv_sec = msg.entry_v1.sec; + lastTimestamp.tv_nsec = msg.entry_v1.nsec; + + // format a BinaryLogEntry + long long token = proto.start(LogProto::BINARY_LOGS); + proto.write(BinaryLogEntry::SEC, msg.entry_v1.sec); + proto.write(BinaryLogEntry::NANOSEC, msg.entry_v1.nsec); + proto.write(BinaryLogEntry::UID, (int) msg.entry_v4.uid); + proto.write(BinaryLogEntry::PID, msg.entry_v1.pid); + proto.write(BinaryLogEntry::TID, msg.entry_v1.tid); + proto.write(BinaryLogEntry::TAG_INDEX, get4LE(reinterpret_cast<uint8_t const*>(msg.msg()))); + do { + elem = android_log_read_next(context); + long long elemToken = proto.start(BinaryLogEntry::ELEMS); + switch (elem.type) { + case EVENT_TYPE_INT: + proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_INT); + proto.write(BinaryLogEntry::Elem::VAL_INT32, (int) elem.data.int32); + break; + case EVENT_TYPE_LONG: + proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LONG); + proto.write(BinaryLogEntry::Elem::VAL_INT64, (long long) elem.data.int64); + break; + case EVENT_TYPE_STRING: + proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_STRING); + proto.write(BinaryLogEntry::Elem::VAL_STRING, elem.data.string, elem.len); + break; + case EVENT_TYPE_FLOAT: + proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_FLOAT); + proto.write(BinaryLogEntry::Elem::VAL_FLOAT, elem.data.float32); + break; + case EVENT_TYPE_LIST: + proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST); + break; + case EVENT_TYPE_LIST_STOP: + proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_LIST_STOP); + break; + case EVENT_TYPE_UNKNOWN: + proto.write(BinaryLogEntry::Elem::TYPE, BinaryLogEntry::Elem::EVENT_TYPE_UNKNOWN); + break; + } + proto.end(elemToken); + } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete); + proto.end(token); + if (context) { + android_log_destroy(&context); + } + } else { + AndroidLogEntry entry; + err = android_log_processLogBuffer(&msg.entry_v1, &entry); + if (err != NO_ERROR) { + ALOGE("LogSection %s: fails to process to an entry.\n", this->name.string()); + break; + } + lastTimestamp.tv_sec = entry.tv_sec; + lastTimestamp.tv_nsec = entry.tv_nsec; + + // format a TextLogEntry + long long token = proto.start(LogProto::TEXT_LOGS); + proto.write(TextLogEntry::SEC, (long long)entry.tv_sec); + proto.write(TextLogEntry::NANOSEC, (long long)entry.tv_nsec); + proto.write(TextLogEntry::PRIORITY, (int)entry.priority); + proto.write(TextLogEntry::UID, entry.uid); + proto.write(TextLogEntry::PID, entry.pid); + proto.write(TextLogEntry::TID, entry.tid); + proto.write(TextLogEntry::TAG, entry.tag, trimTail(entry.tag, entry.tagLen)); + proto.write(TextLogEntry::LOG, entry.message, trimTail(entry.message, entry.messageLen)); + proto.end(token); + } + } + gLastLogsRetrieved[mLogID] = lastTimestamp; + proto.flush(pipeWriteFd); + return err; +} diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index 64558a6b732b..d440ee92601c 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -19,7 +19,9 @@ #include "Reporter.h" +#include <map> #include <stdarg.h> + #include <utils/String8.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -122,5 +124,24 @@ private: Vector<String16> mArgs; }; +/** + * Section that reads from logd. + */ +class LogSection : public WorkerThreadSection +{ + // global last log retrieved timestamp for each log_id_t. + static map<log_id_t, log_time> gLastLogsRetrieved; + +public: + LogSection(int id, log_id_t logID); + virtual ~LogSection(); + + virtual status_t BlockingCall(int pipeWriteFd) const; + +private: + log_id_t mLogID; + bool mBinary; +}; + #endif // SECTIONS_H diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h index dfd2312df668..ddc0505df331 100644 --- a/cmds/incidentd/src/section_list.h +++ b/cmds/incidentd/src/section_list.h @@ -17,6 +17,8 @@ #ifndef SECTION_LIST_H #define SECTION_LIST_H +#include <log/log_event_list.h> // include log_id_t enums. + #include "Privacy.h" #include "Section.h" diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index 0c7876c1ecfb..cbfb89685de0 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -155,7 +155,7 @@ TEST(SectionTest, CommandSectionIncidentHelperTimeout) { } TEST(SectionTest, CommandSectionBadCommand) { - CommandSection cs(NOOP_PARSER, "echo", "about", NULL); + CommandSection cs(NOOP_PARSER, "echoo", "about", NULL); ReportRequestSet requests; ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests)); } @@ -167,6 +167,26 @@ TEST(SectionTest, CommandSectionBadCommandAndTimeout) { ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); } +TEST(SectionTest, LogSectionBinary) { + LogSection ls(1, LOG_ID_EVENTS); + ReportRequestSet requests; + requests.setMainFd(STDOUT_FILENO); + CaptureStdout(); + ASSERT_EQ(NO_ERROR, ls.Execute(&requests)); + string results = GetCapturedStdout(); + EXPECT_FALSE(results.empty()); +} + +TEST(SectionTest, LogSectionSystem) { + LogSection ls(1, LOG_ID_SYSTEM); + ReportRequestSet requests; + requests.setMainFd(STDOUT_FILENO); + CaptureStdout(); + ASSERT_EQ(NO_ERROR, ls.Execute(&requests)); + string results = GetCapturedStdout(); + EXPECT_FALSE(results.empty()); +} + TEST(SectionTest, TestFilterPiiTaggedFields) { TemporaryFile tf; FileSection fs(NOOP_PARSER, tf.path); diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 991badcdddac..a9e0f233d0dd 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -82,9 +82,7 @@ StatsLogProcessor::~StatsLogProcessor() { void StatsLogProcessor::onAnomalyAlarmFired( const uint64_t timestampNs, unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) { - // TODO: This is a thread-safety issue. mMetricsManagers could change under our feet. - // TODO: Solution? Lock everything! :( - // TODO: Question: Can we replace the other lock (broadcast), or do we need to supplement it? + std::lock_guard<std::mutex> lock(mMetricsMutex); for (const auto& itr : mMetricsManagers) { itr.second->onAnomalyAlarmFired(timestampNs, anomalySet); } @@ -92,11 +90,13 @@ void StatsLogProcessor::onAnomalyAlarmFired( // TODO: what if statsd service restarts? How do we know what logs are already processed before? void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC); // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { pair.second->onLogEvent(msg); - flushIfNecessary(msg.GetTimestampNs(), pair.first, *(pair.second)); + flushIfNecessaryLocked(msg.GetTimestampNs(), pair.first, *(pair.second)); } // Hard-coded logic to update the isolated uid's in the uid-map. // The field numbers need to be currently updated by hand with atoms.proto @@ -116,6 +116,7 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { } void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) { + std::lock_guard<std::mutex> lock(mMetricsMutex); ALOGD("Updated configuration for key %s", key.ToString().c_str()); sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap); auto it = mMetricsManagers.find(key); @@ -142,6 +143,7 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig } size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const { + std::lock_guard<std::mutex> lock(mMetricsMutex); auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); @@ -152,6 +154,7 @@ size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const { void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report) { + std::lock_guard<std::mutex> lock(mMetricsMutex); auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); @@ -165,6 +168,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpT } void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) { + std::lock_guard<std::mutex> lock(mMetricsMutex); auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); @@ -173,9 +177,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outD // This allows another broadcast to be sent within the rate-limit period if we get close to // filling the buffer again soon. - mBroadcastTimesMutex.lock(); mLastBroadcastTimes.erase(key); - mBroadcastTimesMutex.unlock(); ProtoOutputStream proto; @@ -224,6 +226,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outD } void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { + std::lock_guard<std::mutex> lock(mMetricsMutex); auto it = mMetricsManagers.find(key); if (it != mMetricsManagers.end()) { mMetricsManagers.erase(it); @@ -231,14 +234,11 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { } StatsdStats::getInstance().noteConfigRemoved(key); - std::lock_guard<std::mutex> lock(mBroadcastTimesMutex); mLastBroadcastTimes.erase(key); } -void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, const ConfigKey& key, - MetricsManager& metricsManager) { - std::lock_guard<std::mutex> lock(mBroadcastTimesMutex); - +void StatsLogProcessor::flushIfNecessaryLocked( + uint64_t timestampNs, const ConfigKey& key, MetricsManager& metricsManager) { auto lastCheckTime = mLastByteSizeTimes.find(key); if (lastCheckTime != mLastByteSizeTimes.end()) { if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) { @@ -274,6 +274,7 @@ void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, const ConfigKey& void StatsLogProcessor::WriteDataToDisk() { mkdir(STATS_DATA_DIR, S_IRWXU); + std::lock_guard<std::mutex> lock(mMetricsMutex); for (auto& pair : mMetricsManagers) { const ConfigKey& key = pair.first; vector<uint8_t> data; diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index f62fc4e31c0a..b527e2790a0c 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -57,7 +57,7 @@ public: void WriteDataToDisk(); private: - mutable mutex mBroadcastTimesMutex; + mutable mutex mMetricsMutex; std::unordered_map<ConfigKey, sp<MetricsManager>> mMetricsManagers; @@ -72,8 +72,8 @@ private: /* Check if we should send a broadcast if approaching memory limits and if we're over, we * actually delete the data. */ - void flushIfNecessary(uint64_t timestampNs, const ConfigKey& key, - MetricsManager& metricsManager); + void flushIfNecessaryLocked(uint64_t timestampNs, const ConfigKey& key, + MetricsManager& metricsManager); // Function used to send a broadcast so that receiver for the config key can call getData // to retrieve the stored data. diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 3f8a8ffef26a..b54629780318 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -117,11 +117,11 @@ unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( case DurationMetric_AggregationType_SUM: return make_unique<OringDurationTracker>( mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); + mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); case DurationMetric_AggregationType_MAX_SPARSE: return make_unique<MaxDurationTracker>( mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); + mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers); } } diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 842581ed1a9f..023c25e20214 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -62,7 +62,7 @@ class DurationTracker { public: DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, - uint64_t currentBucketStartNs, uint64_t bucketSizeNs, + uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers) : mConfigKey(key), mTrackerId(id), @@ -74,6 +74,7 @@ public: mCurrentBucketStartTimeNs(currentBucketStartNs), mDuration(0), mCurrentBucketNum(0), + mConditionSliced(conditionSliced), mAnomalyTrackers(anomalyTrackers){}; virtual ~DurationTracker(){}; @@ -163,6 +164,8 @@ protected: uint64_t mCurrentBucketNum; + const bool mConditionSliced; + std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers; FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index 94f98ada7014..0c99391d491f 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -28,9 +28,10 @@ MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, + bool conditionSliced, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers) : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - bucketSizeNs, anomalyTrackers) { + bucketSizeNs, conditionSliced, anomalyTrackers) { } bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { @@ -63,7 +64,9 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi } DurationInfo& duration = mInfos[key]; - duration.conditionKeys = conditionKey; + if (mConditionSliced) { + duration.conditionKeys = conditionKey; + } VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition); switch (duration.state) { diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index 68c48cb10a4d..5d3c15804638 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -31,7 +31,7 @@ public: MaxDurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, - uint64_t bucketSizeNs, + uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers); void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index c77d0b70507b..6bf42287e6dd 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -27,10 +27,10 @@ using std::pair; OringDurationTracker::OringDurationTracker( const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, - uint64_t bucketSizeNs, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers) - + uint64_t bucketSizeNs, bool conditionSliced, + const vector<sp<DurationAnomalyTracker>>& anomalyTrackers) : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - bucketSizeNs, anomalyTrackers), + bucketSizeNs, conditionSliced, anomalyTrackers), mStarted(), mPaused() { mLastStartTime = 0; @@ -73,7 +73,7 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi mPaused[key]++; } - if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) { + if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) { mConditionKeyMap[key] = conditionKey; } diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index 7fe649c436e2..293726c590e9 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -30,7 +30,7 @@ public: OringDurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, - uint64_t bucketSizeNs, + uint64_t bucketSizeNs, bool conditionSliced, const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers); void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 5d053e25003d..aab5bedb3cbe 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -60,9 +60,9 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { // Expect only the first flush to trigger a check for byte size since the last two are // rate-limited. EXPECT_CALL(mockMetricsManager, byteSize()).Times(1); - p.flushIfNecessary(99, key, mockMetricsManager); - p.flushIfNecessary(100, key, mockMetricsManager); - p.flushIfNecessary(101, key, mockMetricsManager); + p.flushIfNecessaryLocked(99, key, mockMetricsManager); + p.flushIfNecessaryLocked(100, key, mockMetricsManager); + p.flushIfNecessaryLocked(101, key, mockMetricsManager); } TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { @@ -80,12 +80,12 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95))); // Expect only one broadcast despite always returning a size that should trigger broadcast. - p.flushIfNecessary(1, key, mockMetricsManager); + p.flushIfNecessaryLocked(1, key, mockMetricsManager); EXPECT_EQ(1, broadcastCount); // This next call to flush should not trigger a broadcast. p.mLastByteSizeTimes.clear(); // Force another check for byte size. - p.flushIfNecessary(2, key, mockMetricsManager); + p.flushIfNecessaryLocked(2, key, mockMetricsManager); EXPECT_EQ(1, broadcastCount); } @@ -106,7 +106,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { EXPECT_CALL(mockMetricsManager, onDumpReport(_)).Times(1); // Expect to call the onDumpReport and skip the broadcast. - p.flushIfNecessary(1, key, mockMetricsManager); + p.flushIfNecessaryLocked(1, key, mockMetricsManager); EXPECT_EQ(0, broadcastCount); } diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 704a46691328..f98be1b24f4e 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -50,24 +50,22 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; - ConditionKey conditionKey1; - conditionKey1[StringToId("condition")] = conditionKey; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, false, {}); - tracker.noteStart(key1, true, bucketStartTimeNs, conditionKey1); + tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey()); // Event starts again. This would not change anything as it already starts. - tracker.noteStart(key1, true, bucketStartTimeNs + 3, conditionKey1); + tracker.noteStart(key1, true, bucketStartTimeNs + 3, ConditionKey()); // Stopped. tracker.noteStop(key1, bucketStartTimeNs + 10, false); // Another event starts in this bucket. - tracker.noteStart(key2, true, bucketStartTimeNs + 20, conditionKey1); + tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey()); tracker.noteStop(key2, bucketStartTimeNs + 40, false /*stop all*/); tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); @@ -80,20 +78,18 @@ TEST(MaxDurationTrackerTest, TestStopAll) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; - ConditionKey conditionKey1; - conditionKey1[StringToId("condition")] = conditionKey; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, false, {}); - tracker.noteStart(key1, true, bucketStartTimeNs + 1, conditionKey1); + tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey()); // Another event starts in this bucket. - tracker.noteStart(key2, true, bucketStartTimeNs + 20, conditionKey1); + tracker.noteStart(key2, true, bucketStartTimeNs + 20, ConditionKey()); tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets); tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40); EXPECT_TRUE(tracker.mInfos.empty()); @@ -112,22 +108,20 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; - ConditionKey conditionKey1; - conditionKey1[StringToId("condition")] = conditionKey; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, false, {}); // The event starts. - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, conditionKey1); + tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); // Starts again. Does not change anything. tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1, - conditionKey1); + ConditionKey()); // The event stops at early 4th bucket. tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets); @@ -144,19 +138,17 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; - ConditionKey conditionKey1; - conditionKey1[StringToId("condition")] = conditionKey; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, false, {}); // 2 starts - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, conditionKey1); - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, conditionKey1); + tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey()); + tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, ConditionKey()); // one stop tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/); @@ -196,7 +188,7 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { int64_t metricId = 1; MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, true, {}); EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); @@ -222,22 +214,21 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - ConditionKey conditionKey1; - conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey; + uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, - bucketSizeNs, {anomalyTracker}); + bucketSizeNs, false, {anomalyTracker}); - tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1); + tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey()); tracker.noteStop(key1, eventStartTimeNs + 10, false); EXPECT_EQ(anomalyTracker->mLastAnomalyTimestampNs, -1); EXPECT_EQ(10LL, tracker.mDuration); - tracker.noteStart(key2, true, eventStartTimeNs + 20, conditionKey1); + tracker.noteStart(key2, true, eventStartTimeNs + 20, ConditionKey()); tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets); tracker.noteStop(key2, eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false); EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration); diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 36cdaae01b4f..89c6abe7cefd 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -47,9 +47,6 @@ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); TEST(OringDurationTrackerTest, TestDurationOverlap) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; @@ -58,11 +55,11 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { uint64_t durationTimeNs = 2 * 1000; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketSizeNs, {}); + bucketStartTimeNs, bucketSizeNs, false, {}); - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); + tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, key1); // overlapping wl + tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false); @@ -76,9 +73,6 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { TEST(OringDurationTrackerTest, TestDurationNested) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; @@ -86,10 +80,10 @@ TEST(OringDurationTrackerTest, TestDurationNested) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, false, {}); - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, key1); // overlapping wl + tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); + tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false); tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false); @@ -103,9 +97,6 @@ TEST(OringDurationTrackerTest, TestDurationNested) { TEST(OringDurationTrackerTest, TestStopAll) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; @@ -113,10 +104,10 @@ TEST(OringDurationTrackerTest, TestStopAll) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, false, {}); - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); - tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, key1); // overlapping wl + tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); + tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl tracker.noteStopAll(eventStartTimeNs + 2003); @@ -129,9 +120,6 @@ TEST(OringDurationTrackerTest, TestStopAll) { TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; - unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; @@ -140,12 +128,12 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { uint64_t durationTimeNs = 2 * 1000; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, false, {}); - tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); + tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets); - tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, key1); + tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey()); EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime); EXPECT_EQ(2u, buckets[eventKey].size()); @@ -178,7 +166,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { uint64_t durationTimeNs = 2 * 1000; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketSizeNs, {}); + bucketStartTimeNs, bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); @@ -211,7 +199,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { uint64_t durationTimeNs = 2 * 1000; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, - bucketStartTimeNs, bucketSizeNs, {}); + bucketStartTimeNs, bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); // condition to false; record duration 5n @@ -243,7 +231,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}); + bucketSizeNs, true, {}); tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1); tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1); @@ -270,18 +258,17 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; + uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {anomalyTracker}); + bucketSizeNs, true, {anomalyTracker}); // Nothing in the past bucket. - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, key1); + tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs), tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); @@ -289,7 +276,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { EXPECT_EQ(0u, buckets[eventKey].size()); uint64_t event1StartTimeNs = eventStartTimeNs + 10; - tracker.noteStart(kEventKey1, true, event1StartTimeNs, key1); + tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey()); // No past buckets. The anomaly will happen in bucket #0. EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3), tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs)); @@ -308,7 +295,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { // One past buckets. The anomaly will happen in bucket #1. uint64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15; - tracker.noteStart(kEventKey1, true, event2StartTimeNs, key1); + tracker.noteStart(kEventKey1, true, event2StartTimeNs, ConditionKey()); EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration - bucket1Duration), tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs)); @@ -317,7 +304,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in // bucket #2. uint64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC; - tracker.noteStart(kEventKey1, true, event3StartTimeNs, key1); + tracker.noteStart(kEventKey1, true, event3StartTimeNs, ConditionKey()); EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL), tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs)); } @@ -332,17 +319,16 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - ConditionKey key1; - key1[StringToId("APP_BACKGROUND")] = kConditionKey1; + uint64_t bucketStartTimeNs = 10 * NS_PER_SEC; uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey); OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, - bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); + bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker}); - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, key1); + tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey()); tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 10, false); EXPECT_EQ(anomalyTracker->mLastAnomalyTimestampNs, -1); EXPECT_TRUE(tracker.mStarted.empty()); @@ -350,7 +336,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { EXPECT_EQ(0u, tracker.mStarted.size()); - tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs + 20, key1); + tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs + 20, ConditionKey()); EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); EXPECT_EQ((long long)(51ULL * NS_PER_SEC), (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC)); diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java index 55028ebfa8fd..e96ff01d0850 100644 --- a/core/java/android/annotation/SystemApi.java +++ b/core/java/android/annotation/SystemApi.java @@ -39,6 +39,6 @@ import java.lang.annotation.Target; * @hide */ @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE}) -@Retention(RetentionPolicy.SOURCE) +@Retention(RetentionPolicy.RUNTIME) public @interface SystemApi { } diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 7841b83cf92c..35a21a4eaf9f 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -17,6 +17,7 @@ package android.bluetooth; import android.Manifest; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -103,6 +104,24 @@ public final class BluetoothA2dp implements BluetoothProfile { "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; /** + * Intent used to broadcast the selection of a connected device as active. + * + * <p>This intent will have one extra: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active. </li> + * </ul> + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; + + /** * Intent used to broadcast the change in the Audio Codec state of the * A2DP Source profile. * @@ -425,6 +444,75 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, A2DP audio streaming + * is to the active A2DP Sink device. If a remote device is not + * connected, it cannot be selected as active. + * + * <p> This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device the remote Bluetooth device. Could be null to clear + * the active device and stop streaming audio to a Bluetooth device. + * @return false on immediate error, true otherwise + * @hide + */ + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) log("setActiveDevice(" + device + ")"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled() + && ((device == null) || isValidDevice(device))) { + return mService.setActiveDevice(device); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** + * Get the connected device that is active. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} + * permission. + * + * @return the connected device that is active or null if no device + * is active + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @Nullable + public BluetoothDevice getActiveDevice() { + if (VDBG) log("getActiveDevice()"); + try { + mServiceLock.readLock().lock(); + if (mService != null && isEnabled()) { + return mService.getActiveDevice(); + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return null; + } finally { + mServiceLock.readLock().unlock(); + } + } + + /** * Set priority of the profile * * <p> The device should already be paired. diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 838d3153d54c..55a6b4c6b4d4 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -93,6 +94,23 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; + /** + * Intent used to broadcast the selection of a connected device as active. + * + * <p>This intent will have one extra: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active. </li> + * </ul> + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; /** * Intent used to broadcast that the headset has posted a @@ -983,6 +1001,76 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, in HFP and HSP profiles, + * it is the device used for phone call audio. If a remote device is not + * connected, it cannot be selected as active. + * + * <p> This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device, could be null if phone call audio should not be + * streamed to a headset + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) { + Log.d(TAG, "setActiveDevice: " + device); + } + final IBluetoothHeadset service = mService; + if (service != null && isEnabled() && (device == null || isValidDevice(device))) { + try { + return service.setActiveDevice(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return false; + } + + /** + * Get the connected device that is active. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} + * permission. + * + * @return the connected device that is active or null if no device + * is active. + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH) + public BluetoothDevice getActiveDevice() { + if (VDBG) { + Log.d(TAG, "getActiveDevice"); + } + final IBluetoothHeadset service = mService; + if (service != null && isEnabled()) { + try { + return service.getActiveDevice(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + return null; + } + + /** * check if in-band ringing is supported for this platform. * * @return true if in-band ringing is supported false if in-band ringing is not supported diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java index cee25994a271..8014c94d198d 100644 --- a/core/java/android/content/pm/PackageBackwardCompatibility.java +++ b/core/java/android/content/pm/PackageBackwardCompatibility.java @@ -16,6 +16,8 @@ package android.content.pm; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.pm.PackageParser.Package; import android.os.Build; @@ -56,7 +58,7 @@ public class PackageBackwardCompatibility { boolean apacheHttpLegacyPresent = isLibraryPresent( usesLibraries, usesOptionalLibraries, APACHE_HTTP_LEGACY); if (!apacheHttpLegacyPresent) { - usesLibraries = ArrayUtils.add(usesLibraries, APACHE_HTTP_LEGACY); + usesLibraries = prefix(usesLibraries, APACHE_HTTP_LEGACY); } } @@ -86,4 +88,12 @@ public class PackageBackwardCompatibility { return ArrayUtils.contains(usesLibraries, apacheHttpLegacy) || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy); } + + private static @NonNull <T> ArrayList<T> prefix(@Nullable ArrayList<T> cur, T val) { + if (cur == null) { + cur = new ArrayList<>(); + } + cur.add(0, val); + return cur; + } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 77eb57f25613..367c39dfd8ff 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -85,7 +85,6 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; -import android.util.apk.ApkSignatureSchemeV2Verifier; import android.util.apk.ApkSignatureVerifier; import android.view.Gravity; @@ -112,8 +111,7 @@ import java.lang.reflect.Constructor; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; -import java.security.cert.Certificate; -import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; import java.security.spec.EncodedKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; @@ -158,10 +156,6 @@ public class PackageParser { private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false); - public static final int APK_SIGNING_UNKNOWN = 0; - public static final int APK_SIGNING_V1 = 1; - public static final int APK_SIGNING_V2 = 2; - private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f; // TODO: switch outError users to PackageParserException @@ -477,8 +471,7 @@ public class PackageParser { public final int revisionCode; public final int installLocation; public final VerifierInfo[] verifiers; - public final Signature[] signatures; - public final Certificate[][] certificates; + public final SigningDetails signingDetails; public final boolean coreApp; public final boolean debuggable; public final boolean multiArch; @@ -486,10 +479,11 @@ public class PackageParser { public final boolean extractNativeLibs; public final boolean isolatedSplits; - public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit, + public ApkLite(String codePath, String packageName, String splitName, + boolean isFeatureSplit, String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor, int revisionCode, int installLocation, List<VerifierInfo> verifiers, - Signature[] signatures, Certificate[][] certificates, boolean coreApp, + SigningDetails signingDetails, boolean coreApp, boolean debuggable, boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs, boolean isolatedSplits) { this.codePath = codePath; @@ -502,9 +496,8 @@ public class PackageParser { this.versionCodeMajor = versionCodeMajor; this.revisionCode = revisionCode; this.installLocation = installLocation; + this.signingDetails = signingDetails; this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]); - this.signatures = signatures; - this.certificates = certificates; this.coreApp = coreApp; this.debuggable = debuggable; this.multiArch = multiArch; @@ -807,10 +800,10 @@ public class PackageParser { } } if ((flags&PackageManager.GET_SIGNATURES) != 0) { - int N = (p.mSignatures != null) ? p.mSignatures.length : 0; - if (N > 0) { - pi.signatures = new Signature[N]; - System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N); + if (p.mSigningDetails.hasSignatures()) { + int numberOfSigs = p.mSigningDetails.signatures.length; + pi.signatures = new Signature[numberOfSigs]; + System.arraycopy(p.mSigningDetails.signatures, 0, pi.signatures, 0, numberOfSigs); } } return pi; @@ -1349,7 +1342,7 @@ public class PackageParser { pkg.setVolumeUuid(volumeUuid); pkg.setApplicationVolumeUuid(volumeUuid); pkg.setBaseCodePath(apkPath); - pkg.setSignatures(null); + pkg.setSigningDetails(SigningDetails.UNKNOWN); return pkg; @@ -1469,57 +1462,19 @@ public class PackageParser { return pkg; } - public static int getApkSigningVersion(Package pkg) { - try { - if (ApkSignatureSchemeV2Verifier.hasSignature(pkg.baseCodePath)) { - return APK_SIGNING_V2; - } - return APK_SIGNING_V1; - } catch (IOException e) { - } - return APK_SIGNING_UNKNOWN; - } - - /** - * Populates the correct packages fields with the given certificates. - * <p> - * This is useful when we've already processed the certificates [such as during package - * installation through an installer session]. We don't re-process the archive and - * simply populate the correct fields. - */ - public static void populateCertificates(Package pkg, Certificate[][] certificates) - throws PackageParserException { - pkg.mCertificates = null; - pkg.mSignatures = null; - pkg.mSigningKeys = null; - - pkg.mCertificates = certificates; - try { - pkg.mSignatures = ApkSignatureVerifier.convertToSignatures(certificates); - } catch (CertificateEncodingException e) { - // certificates weren't encoded properly; something went wrong - throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, - "Failed to collect certificates from " + pkg.baseCodePath, e); - } - pkg.mSigningKeys = new ArraySet<>(certificates.length); - for (int i = 0; i < certificates.length; i++) { - Certificate[] signerCerts = certificates[i]; - Certificate signerCert = signerCerts[0]; - pkg.mSigningKeys.add(signerCert.getPublicKey()); - } - // add signatures to child packages - final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; - for (int i = 0; i < childCount; i++) { - Package childPkg = pkg.childPackages.get(i); - childPkg.mCertificates = pkg.mCertificates; - childPkg.mSignatures = pkg.mSignatures; - childPkg.mSigningKeys = pkg.mSigningKeys; + /** Parses the public keys from the set of signatures. */ + public static ArraySet<PublicKey> toSigningKeys(Signature[] signatures) + throws CertificateException { + ArraySet<PublicKey> keys = new ArraySet<>(signatures.length); + for (int i = 0; i < signatures.length; i++) { + keys.add(signatures[i].getPublicKey()); } + return keys; } /** * Collect certificates from all the APKs described in the given package, - * populating {@link Package#mSignatures}. Also asserts that all APK + * populating {@link Package#mSigningDetails}. Also asserts that all APK * contents are signed correctly and consistently. */ public static void collectCertificates(Package pkg, @ParseFlags int parseFlags) @@ -1528,17 +1483,13 @@ public class PackageParser { final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { Package childPkg = pkg.childPackages.get(i); - childPkg.mCertificates = pkg.mCertificates; - childPkg.mSignatures = pkg.mSignatures; - childPkg.mSigningKeys = pkg.mSigningKeys; + childPkg.mSigningDetails = pkg.mSigningDetails; } } private static void collectCertificatesInternal(Package pkg, @ParseFlags int parseFlags) throws PackageParserException { - pkg.mCertificates = null; - pkg.mSignatures = null; - pkg.mSigningKeys = null; + pkg.mSigningDetails = SigningDetails.UNKNOWN; Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); try { @@ -1558,12 +1509,12 @@ public class PackageParser { throws PackageParserException { final String apkPath = apkFile.getAbsolutePath(); - int minSignatureScheme = ApkSignatureVerifier.VERSION_JAR_SIGNATURE_SCHEME; + int minSignatureScheme = SigningDetails.SignatureSchemeVersion.JAR; if (pkg.applicationInfo.isStaticSharedLibrary()) { // must use v2 signing scheme - minSignatureScheme = ApkSignatureVerifier.VERSION_APK_SIGNATURE_SCHEME_V2; + minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2; } - ApkSignatureVerifier.Result verified; + SigningDetails verified; if ((parseFlags & PARSE_IS_SYSTEM_DIR) != 0) { // systemDir APKs are already trusted, save time by not verifying verified = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts( @@ -1572,7 +1523,7 @@ public class PackageParser { verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme); } if (verified.signatureSchemeVersion - < ApkSignatureVerifier.VERSION_APK_SIGNATURE_SCHEME_V2) { + < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) { // TODO (b/68860689): move this logic to packagemanagerserivce if ((parseFlags & PARSE_IS_EPHEMERAL) != 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, @@ -1583,17 +1534,10 @@ public class PackageParser { // Verify that entries are signed consistently with the first pkg // we encountered. Note that for splits, certificates may have // already been populated during an earlier parse of a base APK. - if (pkg.mCertificates == null) { - pkg.mCertificates = verified.certs; - pkg.mSignatures = verified.sigs; - pkg.mSigningKeys = new ArraySet<>(verified.certs.length); - for (int i = 0; i < verified.certs.length; i++) { - Certificate[] signerCerts = verified.certs[i]; - Certificate signerCert = signerCerts[0]; - pkg.mSigningKeys.add(signerCert.getPublicKey()); - } + if (pkg.mSigningDetails == SigningDetails.UNKNOWN) { + pkg.mSigningDetails = verified; } else { - if (!Signature.areExactMatch(pkg.mSignatures, verified.sigs)) { + if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) { throw new PackageParserException( INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, apkPath + " has mismatched certificates"); @@ -1655,8 +1599,7 @@ public class PackageParser { parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); - final Signature[] signatures; - final Certificate[][] certificates; + final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { // TODO: factor signature related items out of Package object final Package tempPkg = new Package((String) null); @@ -1666,15 +1609,13 @@ public class PackageParser { } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - signatures = tempPkg.mSignatures; - certificates = tempPkg.mCertificates; + signingDetails = tempPkg.mSigningDetails; } else { - signatures = null; - certificates = null; + signingDetails = SigningDetails.UNKNOWN; } final AttributeSet attrs = parser; - return parseApkLite(apkPath, parser, attrs, signatures, certificates); + return parseApkLite(apkPath, parser, attrs, signingDetails); } catch (XmlPullParserException | IOException | RuntimeException e) { Slog.w(TAG, "Failed to parse " + apkPath, e); @@ -1761,7 +1702,7 @@ public class PackageParser { } private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs, - Signature[] signatures, Certificate[][] certificates) + SigningDetails signingDetails) throws IOException, XmlPullParserException, PackageParserException { final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs); @@ -1854,7 +1795,7 @@ public class PackageParser { return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit, configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode, - installLocation, verifiers, signatures, certificates, coreApp, debuggable, + installLocation, verifiers, signingDetails, coreApp, debuggable, multiArch, use32bitAbi, extractNativeLibs, isolatedSplits); } @@ -5734,6 +5675,117 @@ public class PackageParser { return true; } + /** A container for signing-related data of an application package. */ + public static final class SigningDetails implements Parcelable { + + @IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN, + SigningDetails.SignatureSchemeVersion.JAR, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3}) + public @interface SignatureSchemeVersion { + int UNKNOWN = 0; + int JAR = 1; + int SIGNING_BLOCK_V2 = 2; + int SIGNING_BLOCK_V3 = 3; + } + + @Nullable + public final Signature[] signatures; + @SignatureSchemeVersion + public final int signatureSchemeVersion; + @Nullable + public final ArraySet<PublicKey> publicKeys; + + /** A representation of unknown signing details. Use instead of null. */ + public static final SigningDetails UNKNOWN = + new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null); + + @VisibleForTesting + public SigningDetails(Signature[] signatures, + @SignatureSchemeVersion int signatureSchemeVersion, + ArraySet<PublicKey> keys) { + this.signatures = signatures; + this.signatureSchemeVersion = signatureSchemeVersion; + this.publicKeys = keys; + } + + public SigningDetails(Signature[] signatures, + @SignatureSchemeVersion int signatureSchemeVersion) + throws CertificateException { + this(signatures, signatureSchemeVersion, toSigningKeys(signatures)); + } + + /** Returns true if the signing details have one or more signatures. */ + public boolean hasSignatures() { + return signatures != null && signatures.length > 0; + } + + /** Returns true if the signatures in this and other match exactly. */ + public boolean signaturesMatchExactly(SigningDetails other) { + return Signature.areExactMatch(this.signatures, other.signatures); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + boolean isUnknown = UNKNOWN == this; + dest.writeBoolean(isUnknown); + if (isUnknown) { + return; + } + dest.writeTypedArray(this.signatures, flags); + dest.writeInt(this.signatureSchemeVersion); + dest.writeArraySet(this.publicKeys); + } + + protected SigningDetails(Parcel in) { + final ClassLoader boot = Object.class.getClassLoader(); + this.signatures = in.createTypedArray(Signature.CREATOR); + this.signatureSchemeVersion = in.readInt(); + this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot); + } + + public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() { + @Override + public SigningDetails createFromParcel(Parcel source) { + if (source.readBoolean()) { + return UNKNOWN; + } + return new SigningDetails(source); + } + + @Override + public SigningDetails[] newArray(int size) { + return new SigningDetails[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SigningDetails)) return false; + + SigningDetails that = (SigningDetails) o; + + if (signatureSchemeVersion != that.signatureSchemeVersion) return false; + if (!Signature.areExactMatch(signatures, that.signatures)) return false; + return publicKeys != null ? publicKeys.equals(that.publicKeys) + : that.publicKeys == null; + } + + @Override + public int hashCode() { + int result = +Arrays.hashCode(signatures); + result = 31 * result + signatureSchemeVersion; + result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0); + return result; + } + } + /** * Representation of a full package parsed from APK files on disk. A package * consists of a single base APK, and zero or more split APKs. @@ -5840,8 +5892,7 @@ public class PackageParser { public int mSharedUserLabel; // Signatures that were read from the package. - public Signature[] mSignatures; - public Certificate[][] mCertificates; + @NonNull public SigningDetails mSigningDetails = SigningDetails.UNKNOWN; // For use by package manager service for quick lookup of // preferred up order. @@ -5893,7 +5944,6 @@ public class PackageParser { /** * Data used to feed the KeySetManagerService */ - public ArraySet<PublicKey> mSigningKeys; public ArraySet<String> mUpgradeKeySets; public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping; @@ -6037,12 +6087,13 @@ public class PackageParser { } } - public void setSignatures(Signature[] signatures) { - this.mSignatures = signatures; + /** Sets signing details on the package and any of its children. */ + public void setSigningDetails(@NonNull SigningDetails signingDetails) { + mSigningDetails = signingDetails; if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { - childPackages.get(i).mSignatures = signatures; + childPackages.get(i).mSigningDetails = signingDetails; } } } @@ -6348,8 +6399,7 @@ public class PackageParser { } mSharedUserLabel = dest.readInt(); - mSignatures = (Signature[]) dest.readParcelableArray(boot, Signature.class); - mCertificates = (Certificate[][]) dest.readSerializable(); + mSigningDetails = dest.readParcelable(boot); mPreferredOrder = dest.readInt(); @@ -6389,7 +6439,6 @@ public class PackageParser { mTrustedOverlay = (dest.readInt() == 1); mCompileSdkVersion = dest.readInt(); mCompileSdkVersionCodename = dest.readString(); - mSigningKeys = (ArraySet<PublicKey>) dest.readArraySet(boot); mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot); mKeySetMapping = readKeySetMapping(dest); @@ -6489,8 +6538,7 @@ public class PackageParser { dest.writeString(mSharedUserId); dest.writeInt(mSharedUserLabel); - dest.writeParcelableArray(mSignatures, flags); - dest.writeSerializable(mCertificates); + dest.writeParcelable(mSigningDetails, flags); dest.writeInt(mPreferredOrder); @@ -6515,7 +6563,6 @@ public class PackageParser { dest.writeInt(mTrustedOverlay ? 1 : 0); dest.writeInt(mCompileSdkVersion); dest.writeString(mCompileSdkVersionCodename); - dest.writeArraySet(mSigningKeys); dest.writeArraySet(mUpgradeKeySets); writeKeySetMapping(dest, mKeySetMapping); dest.writeString(cpuAbiOverride); diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java index e6cd3fc130a0..f54ceb5c142a 100644 --- a/core/java/android/net/IpSecConfig.java +++ b/core/java/android/net/IpSecConfig.java @@ -102,17 +102,11 @@ public final class IpSecConfig implements Parcelable { /** Set the local IP address for Tunnel mode */ public void setLocalAddress(String localAddress) { - if (localAddress == null) { - throw new IllegalArgumentException("localAddress may not be null!"); - } mLocalAddress = localAddress; } /** Set the remote IP address for this IPsec transform */ public void setRemoteAddress(String remoteAddress) { - if (remoteAddress == null) { - throw new IllegalArgumentException("remoteAddress may not be null!"); - } mRemoteAddress = remoteAddress; } diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 6a4b8914780c..34cfa9b2153d 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -69,7 +69,7 @@ public final class IpSecManager { } /** @hide */ - public static final int INVALID_RESOURCE_ID = 0; + public static final int INVALID_RESOURCE_ID = -1; /** * Thrown to indicate that a requested SPI is in use. @@ -128,7 +128,7 @@ public final class IpSecManager { private final InetAddress mRemoteAddress; private final CloseGuard mCloseGuard = CloseGuard.get(); private int mSpi = INVALID_SECURITY_PARAMETER_INDEX; - private int mResourceId; + private int mResourceId = INVALID_RESOURCE_ID; /** Get the underlying SPI held by this object. */ public int getSpi() { @@ -146,6 +146,7 @@ public final class IpSecManager { public void close() { try { mService.releaseSecurityParameterIndex(mResourceId); + mResourceId = INVALID_RESOURCE_ID; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -501,7 +502,7 @@ public final class IpSecManager { public static final class UdpEncapsulationSocket implements AutoCloseable { private final ParcelFileDescriptor mPfd; private final IIpSecService mService; - private final int mResourceId; + private int mResourceId = INVALID_RESOURCE_ID; private final int mPort; private final CloseGuard mCloseGuard = CloseGuard.get(); @@ -554,6 +555,7 @@ public final class IpSecManager { public void close() throws IOException { try { mService.closeUdpEncapsulationSocket(mResourceId); + mResourceId = INVALID_RESOURCE_ID; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 7cd742b417a4..102ba6d94faa 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -347,6 +347,9 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform.Builder setSpi( @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) { + if (spi.getResourceId() == INVALID_RESOURCE_ID) { + throw new IllegalArgumentException("Invalid SecurityParameterIndex"); + } mConfig.setSpiResourceId(direction, spi.getResourceId()); return this; } @@ -381,6 +384,9 @@ public final class IpSecTransform implements AutoCloseable { public IpSecTransform.Builder setIpv4Encapsulation( IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) { mConfig.setEncapType(ENCAP_ESPINUDP); + if (localSocket.getResourceId() == INVALID_RESOURCE_ID) { + throw new IllegalArgumentException("Invalid UdpEncapsulationSocket"); + } mConfig.setEncapSocketResourceId(localSocket.getResourceId()); mConfig.setEncapRemotePort(remotePort); return this; @@ -426,6 +432,9 @@ public final class IpSecTransform implements AutoCloseable { public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress) throws IpSecManager.ResourceUnavailableException, IpSecManager.SpiUnavailableException, IOException { + if (remoteAddress == null) { + throw new IllegalArgumentException("Remote address may not be null or empty!"); + } mConfig.setMode(MODE_TRANSPORT); mConfig.setRemoteAddress(remoteAddress.getHostAddress()); // FIXME: modifying a builder after calling build can change the built transform. @@ -447,8 +456,12 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform buildTunnelModeTransform( InetAddress localAddress, InetAddress remoteAddress) { - // FIXME: argument validation here - // throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); + if (localAddress == null) { + throw new IllegalArgumentException("Local address may not be null or empty!"); + } + if (remoteAddress == null) { + throw new IllegalArgumentException("Remote address may not be null or empty!"); + } mConfig.setLocalAddress(localAddress.getHostAddress()); mConfig.setRemoteAddress(remoteAddress.getHostAddress()); mConfig.setMode(MODE_TUNNEL); diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java index a080c8dcbcc5..4d7d9319c94e 100644 --- a/core/java/android/os/HidlSupport.java +++ b/core/java/android/os/HidlSupport.java @@ -86,6 +86,25 @@ public class HidlSupport { } /** + * Class which can be used to fetch an object out of a lambda. Fetching an object + * out of a local scope with HIDL is a common operation (although usually it can + * and should be avoided). + * + * @param <E> Inner object type. + */ + public static final class Mutable<E> { + public E value; + + public Mutable() { + value = null; + } + + public Mutable(E value) { + this.value = value; + } + } + + /** * Similar to Arrays.deepHashCode, but also take care of lists. */ public static int deepHashCode(Object o) { diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java index 401b4a36a743..bb043b0109d5 100644 --- a/core/java/android/os/WorkSource.java +++ b/core/java/android/os/WorkSource.java @@ -407,11 +407,11 @@ public class WorkSource implements Parcelable { } public boolean remove(WorkSource other) { - if (mNum <= 0 || other.mNum <= 0) { + if (isEmpty() || other.isEmpty()) { return false; } - boolean uidRemoved = false; + boolean uidRemoved; if (mNames == null && other.mNames == null) { uidRemoved = removeUids(other); } else { @@ -427,13 +427,8 @@ public class WorkSource implements Parcelable { } boolean chainRemoved = false; - if (other.mChains != null) { - if (mChains != null) { - chainRemoved = mChains.removeAll(other.mChains); - } - } else if (mChains != null) { - mChains.clear(); - chainRemoved = true; + if (other.mChains != null && mChains != null) { + chainRemoved = mChains.removeAll(other.mChains); } return uidRemoved || chainRemoved; diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 9de1223515f7..f4deeedae697 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -114,6 +114,8 @@ public class StorageManager { /** {@hide} */ public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; /** {@hide} */ + public static final String PROP_HAS_RESERVED = "vold.has_reserved"; + /** {@hide} */ public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; /** {@hide} */ public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 009fc395ff3b..0850bd997c3a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7191,13 +7191,6 @@ public final class Settings { public static final String QS_TILES = "sysui_qs_tiles"; /** - * Whether preloaded APKs have been installed for the user. - * @hide - */ - public static final String DEMO_USER_SETUP_COMPLETE - = "demo_user_setup_complete"; - - /** * Specifies whether the web action API is enabled. * * @hide @@ -9782,6 +9775,22 @@ public final class Settings { public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants"; /** + * BatteryStats specific settings. + * This is encoded as a key=value list, separated by commas. Ex: "foo=1,bar=true" + * + * The following keys are supported: + * <pre> + * track_cpu_times_by_proc_state (boolean) + * </pre> + * + * <p> + * Type: string + * @hide + * see also com.android.internal.os.BatteryStatsImpl.Constants + */ + public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants"; + + /** * Whether or not App Standby feature is enabled. This controls throttling of apps * based on usage patterns and predictions. * Type: int (0 for false, 1 for true) diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 6bca37af376a..18431cacbfaf 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -1096,6 +1096,11 @@ public class DynamicLayout extends Layout { public void onSpanChanged(Spannable s, Object o, int start, int end, int nstart, int nend) { if (o instanceof UpdateLayout) { + if (start > end) { + // Bug: 67926915 start cannot be determined, fallback to reflow from start + // instead of causing an exception + start = 0; + } reflow(s, start, end - start, end - start); reflow(s, nstart, nend - nstart, nend - nstart); } diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java index 0a00794a1471..7eef63efd14d 100644 --- a/core/java/android/util/KeyValueListParser.java +++ b/core/java/android/util/KeyValueListParser.java @@ -17,6 +17,9 @@ package android.util; import android.text.TextUtils; +import java.time.Duration; +import java.time.format.DateTimeParseException; + /** * Parses a list of key=value pairs, separated by some delimiter, and puts the results in * an internal Map. Values can be then queried by key, or if not found, a default value @@ -189,4 +192,24 @@ public class KeyValueListParser { public String keyAt(int index) { return mValues.keyAt(index); } + + /** + * {@hide} + * Parse a duration in millis based on java.time.Duration or just a number (millis) + */ + public long getDurationMillis(String key, long def) { + String value = mValues.get(key); + if (value != null) { + try { + if (value.startsWith("P") || value.startsWith("p")) { + return Duration.parse(value).toMillis(); + } else { + return Long.parseLong(value); + } + } catch (NumberFormatException | DateTimeParseException e) { + // fallthrough + } + } + return def; + } } diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index 81467292d491..555c4740389a 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -25,6 +25,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; import android.content.pm.Signature; import android.os.Trace; import android.util.jar.StrictJarFile; @@ -52,10 +53,6 @@ import java.util.zip.ZipEntry; */ public class ApkSignatureVerifier { - public static final int VERSION_JAR_SIGNATURE_SCHEME = 1; - public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; - public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3; - private static final AtomicReference<byte[]> sBuffer = new AtomicReference<>(); /** @@ -63,10 +60,11 @@ public class ApkSignatureVerifier { * * @throws PackageParserException if the APK's signature failed to verify. */ - public static Result verify(String apkPath, int minSignatureSchemeVersion) + public static PackageParser.SigningDetails verify(String apkPath, + @SignatureSchemeVersion int minSignatureSchemeVersion) throws PackageParserException { - if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V3) { + if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) { // V3 and before are older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No signature found in package of version " + minSignatureSchemeVersion @@ -80,10 +78,11 @@ public class ApkSignatureVerifier { ApkSignatureSchemeV3Verifier.verify(apkPath); Certificate[][] signerCerts = new Certificate[][] { vSigner.certs }; Signature[] signerSigs = convertToSignatures(signerCerts); - return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V3); + return new PackageParser.SigningDetails(signerSigs, + SignatureSchemeVersion.SIGNING_BLOCK_V3); } catch (SignatureNotFoundException e) { // not signed with v2, try older if allowed - if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V3) { + if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v3 signature in package " + apkPath, e); } @@ -97,7 +96,7 @@ public class ApkSignatureVerifier { } // redundant, protective version check - if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V2) { + if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) { // V2 and before are older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No signature found in package of version " + minSignatureSchemeVersion @@ -110,10 +109,11 @@ public class ApkSignatureVerifier { Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath); Signature[] signerSigs = convertToSignatures(signerCerts); - return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V2); + return new PackageParser.SigningDetails( + signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V2); } catch (SignatureNotFoundException e) { // not signed with v2, try older if allowed - if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V2) { + if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v2 signature in package " + apkPath, e); } @@ -127,7 +127,7 @@ public class ApkSignatureVerifier { } // redundant, protective version check - if (minSignatureSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME) { + if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) { // V1 and is older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No signature found in package of version " + minSignatureSchemeVersion @@ -145,7 +145,8 @@ public class ApkSignatureVerifier { * * @throws PackageParserException if there was a problem collecting certificates */ - private static Result verifyV1Signature(String apkPath, boolean verifyFull) + private static PackageParser.SigningDetails verifyV1Signature( + String apkPath, boolean verifyFull) throws PackageParserException { StrictJarFile jarFile = null; @@ -211,7 +212,7 @@ public class ApkSignatureVerifier { } } } - return new Result(lastCerts, lastSigs, VERSION_JAR_SIGNATURE_SCHEME); + return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR); } catch (GeneralSecurityException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, "Failed to collect certificates from " + apkPath, e); @@ -289,10 +290,11 @@ public class ApkSignatureVerifier { * @throws PackageParserException if the APK's signature failed to verify. * or greater is not found, except in the case of no JAR signature. */ - public static Result plsCertsNoVerifyOnlyCerts(String apkPath, int minSignatureSchemeVersion) + public static PackageParser.SigningDetails plsCertsNoVerifyOnlyCerts( + String apkPath, int minSignatureSchemeVersion) throws PackageParserException { - if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V3) { + if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) { // V3 and before are older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No signature found in package of version " + minSignatureSchemeVersion @@ -306,10 +308,11 @@ public class ApkSignatureVerifier { ApkSignatureSchemeV3Verifier.plsCertsNoVerifyOnlyCerts(apkPath); Certificate[][] signerCerts = new Certificate[][] { vSigner.certs }; Signature[] signerSigs = convertToSignatures(signerCerts); - return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V3); + return new PackageParser.SigningDetails(signerSigs, + SignatureSchemeVersion.SIGNING_BLOCK_V3); } catch (SignatureNotFoundException e) { // not signed with v2, try older if allowed - if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V3) { + if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v3 signature in package " + apkPath, e); } @@ -323,7 +326,7 @@ public class ApkSignatureVerifier { } // redundant, protective version check - if (minSignatureSchemeVersion > VERSION_APK_SIGNATURE_SCHEME_V2) { + if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) { // V2 and before are older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No signature found in package of version " + minSignatureSchemeVersion @@ -336,10 +339,11 @@ public class ApkSignatureVerifier { Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.plsCertsNoVerifyOnlyCerts(apkPath); Signature[] signerSigs = convertToSignatures(signerCerts); - return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V2); + return new PackageParser.SigningDetails(signerSigs, + SignatureSchemeVersion.SIGNING_BLOCK_V2); } catch (SignatureNotFoundException e) { // not signed with v2, try older if allowed - if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V2) { + if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v2 signature in package " + apkPath, e); } @@ -353,7 +357,7 @@ public class ApkSignatureVerifier { } // redundant, protective version check - if (minSignatureSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME) { + if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) { // V1 and is older than the requested minimum signing version throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No signature found in package of version " + minSignatureSchemeVersion @@ -363,19 +367,4 @@ public class ApkSignatureVerifier { // v2 didn't work, try jarsigner return verifyV1Signature(apkPath, false); } - - /** - * Result of a successful APK verification operation. - */ - public static class Result { - public final Certificate[][] certs; - public final Signature[] sigs; - public final int signatureSchemeVersion; - - public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) { - this.certs = certs; - this.sigs = sigs; - this.signatureSchemeVersion = signingVersion; - } - } } diff --git a/core/java/android/webkit/FindAddress.java b/core/java/android/webkit/FindAddress.java new file mode 100644 index 000000000000..31b242739882 --- /dev/null +++ b/core/java/android/webkit/FindAddress.java @@ -0,0 +1,478 @@ +/* + * 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. + */ + +package android.webkit; + +import java.util.Locale; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Java implementation of legacy WebView.findAddress algorithm. + * + * @hide + */ +class FindAddress { + static class ZipRange { + int mLow; + int mHigh; + int mException1; + int mException2; + ZipRange(int low, int high, int exception1, int exception2) { + mLow = low; + mHigh = high; + mException1 = exception1; + mException2 = exception1; + } + boolean matches(String zipCode) { + int prefix = Integer.parseInt(zipCode.substring(0, 2)); + return (mLow <= prefix && prefix <= mHigh) || prefix == mException1 + || prefix == mException2; + } + } + + // Addresses consist of at least this many words, not including state and zip code. + private static final int MIN_ADDRESS_WORDS = 4; + + // Adddresses consist of at most this many words, not including state and zip code. + private static final int MAX_ADDRESS_WORDS = 14; + + // Addresses consist of at most this many lines. + private static final int MAX_ADDRESS_LINES = 5; + + // No words in an address are longer than this many characters. + private static final int kMaxAddressNameWordLength = 25; + + // Location name should be in the first MAX_LOCATION_NAME_DISTANCE words + private static final int MAX_LOCATION_NAME_DISTANCE = 5; + + private static final ZipRange[] sStateZipCodeRanges = { + new ZipRange(99, 99, -1, -1), // AK Alaska. + new ZipRange(35, 36, -1, -1), // AL Alabama. + new ZipRange(71, 72, -1, -1), // AR Arkansas. + new ZipRange(96, 96, -1, -1), // AS American Samoa. + new ZipRange(85, 86, -1, -1), // AZ Arizona. + new ZipRange(90, 96, -1, -1), // CA California. + new ZipRange(80, 81, -1, -1), // CO Colorado. + new ZipRange(6, 6, -1, -1), // CT Connecticut. + new ZipRange(20, 20, -1, -1), // DC District of Columbia. + new ZipRange(19, 19, -1, -1), // DE Delaware. + new ZipRange(32, 34, -1, -1), // FL Florida. + new ZipRange(96, 96, -1, -1), // FM Federated States of Micronesia. + new ZipRange(30, 31, -1, -1), // GA Georgia. + new ZipRange(96, 96, -1, -1), // GU Guam. + new ZipRange(96, 96, -1, -1), // HI Hawaii. + new ZipRange(50, 52, -1, -1), // IA Iowa. + new ZipRange(83, 83, -1, -1), // ID Idaho. + new ZipRange(60, 62, -1, -1), // IL Illinois. + new ZipRange(46, 47, -1, -1), // IN Indiana. + new ZipRange(66, 67, 73, -1), // KS Kansas. + new ZipRange(40, 42, -1, -1), // KY Kentucky. + new ZipRange(70, 71, -1, -1), // LA Louisiana. + new ZipRange(1, 2, -1, -1), // MA Massachusetts. + new ZipRange(20, 21, -1, -1), // MD Maryland. + new ZipRange(3, 4, -1, -1), // ME Maine. + new ZipRange(96, 96, -1, -1), // MH Marshall Islands. + new ZipRange(48, 49, -1, -1), // MI Michigan. + new ZipRange(55, 56, -1, -1), // MN Minnesota. + new ZipRange(63, 65, -1, -1), // MO Missouri. + new ZipRange(96, 96, -1, -1), // MP Northern Mariana Islands. + new ZipRange(38, 39, -1, -1), // MS Mississippi. + new ZipRange(55, 56, -1, -1), // MT Montana. + new ZipRange(27, 28, -1, -1), // NC North Carolina. + new ZipRange(58, 58, -1, -1), // ND North Dakota. + new ZipRange(68, 69, -1, -1), // NE Nebraska. + new ZipRange(3, 4, -1, -1), // NH New Hampshire. + new ZipRange(7, 8, -1, -1), // NJ New Jersey. + new ZipRange(87, 88, 86, -1), // NM New Mexico. + new ZipRange(88, 89, 96, -1), // NV Nevada. + new ZipRange(10, 14, 0, 6), // NY New York. + new ZipRange(43, 45, -1, -1), // OH Ohio. + new ZipRange(73, 74, -1, -1), // OK Oklahoma. + new ZipRange(97, 97, -1, -1), // OR Oregon. + new ZipRange(15, 19, -1, -1), // PA Pennsylvania. + new ZipRange(6, 6, 0, 9), // PR Puerto Rico. + new ZipRange(96, 96, -1, -1), // PW Palau. + new ZipRange(2, 2, -1, -1), // RI Rhode Island. + new ZipRange(29, 29, -1, -1), // SC South Carolina. + new ZipRange(57, 57, -1, -1), // SD South Dakota. + new ZipRange(37, 38, -1, -1), // TN Tennessee. + new ZipRange(75, 79, 87, 88), // TX Texas. + new ZipRange(84, 84, -1, -1), // UT Utah. + new ZipRange(22, 24, 20, -1), // VA Virginia. + new ZipRange(6, 9, -1, -1), // VI Virgin Islands. + new ZipRange(5, 5, -1, -1), // VT Vermont. + new ZipRange(98, 99, -1, -1), // WA Washington. + new ZipRange(53, 54, -1, -1), // WI Wisconsin. + new ZipRange(24, 26, -1, -1), // WV West Virginia. + new ZipRange(82, 83, -1, -1) // WY Wyoming. + }; + + // Newlines + private static final String NL = "\n\u000B\u000C\r\u0085\u2028\u2029"; + + // Space characters + private static final String SP = "\u0009\u0020\u00A0\u1680\u2000\u2001" + + "\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F" + + "\u205F\u3000"; + + // Whitespace + private static final String WS = SP + NL; + + // Characters that are considered word delimiters. + private static final String WORD_DELIM = ",*\u2022" + WS; + + // Lookahead for word end. + private static final String WORD_END = "(?=[" + WORD_DELIM + "]|$)"; + + // Address words are a sequence of non-delimiter characters. + private static final Pattern sWordRe = + Pattern.compile("[^" + WORD_DELIM + "]+" + WORD_END, Pattern.CASE_INSENSITIVE); + + // Characters that are considered suffix delimiters for house numbers. + private static final String HOUSE_POST_DELIM = ",\"'" + WS; + + // Lookahead for house end. + private static final String HOUSE_END = "(?=[" + HOUSE_POST_DELIM + "]|$)"; + + // Characters that are considered prefix delimiters for house numbers. + private static final String HOUSE_PRE_DELIM = ":" + HOUSE_POST_DELIM; + + // A house number component is "one" or a number, optionally + // followed by a single alphabetic character, or + private static final String HOUSE_COMPONENT = "(?:one|\\d+([a-z](?=[^a-z]|$)|st|nd|rd|th)?)"; + + // House numbers are a repetition of |HOUSE_COMPONENT|, separated by -, and followed by + // a delimiter character. + private static final Pattern sHouseNumberRe = + Pattern.compile(HOUSE_COMPONENT + "(?:-" + HOUSE_COMPONENT + ")*" + HOUSE_END, + Pattern.CASE_INSENSITIVE); + + // XXX: do we want to accept whitespace other than 0x20 in state names? + private static final Pattern sStateRe = Pattern.compile("(?:" + + "(ak|alaska)|" + + "(al|alabama)|" + + "(ar|arkansas)|" + + "(as|american[" + SP + "]+samoa)|" + + "(az|arizona)|" + + "(ca|california)|" + + "(co|colorado)|" + + "(ct|connecticut)|" + + "(dc|district[" + SP + "]+of[" + SP + "]+columbia)|" + + "(de|delaware)|" + + "(fl|florida)|" + + "(fm|federated[" + SP + "]+states[" + SP + "]+of[" + SP + "]+micronesia)|" + + "(ga|georgia)|" + + "(gu|guam)|" + + "(hi|hawaii)|" + + "(ia|iowa)|" + + "(id|idaho)|" + + "(il|illinois)|" + + "(in|indiana)|" + + "(ks|kansas)|" + + "(ky|kentucky)|" + + "(la|louisiana)|" + + "(ma|massachusetts)|" + + "(md|maryland)|" + + "(me|maine)|" + + "(mh|marshall[" + SP + "]+islands)|" + + "(mi|michigan)|" + + "(mn|minnesota)|" + + "(mo|missouri)|" + + "(mp|northern[" + SP + "]+mariana[" + SP + "]+islands)|" + + "(ms|mississippi)|" + + "(mt|montana)|" + + "(nc|north[" + SP + "]+carolina)|" + + "(nd|north[" + SP + "]+dakota)|" + + "(ne|nebraska)|" + + "(nh|new[" + SP + "]+hampshire)|" + + "(nj|new[" + SP + "]+jersey)|" + + "(nm|new[" + SP + "]+mexico)|" + + "(nv|nevada)|" + + "(ny|new[" + SP + "]+york)|" + + "(oh|ohio)|" + + "(ok|oklahoma)|" + + "(or|oregon)|" + + "(pa|pennsylvania)|" + + "(pr|puerto[" + SP + "]+rico)|" + + "(pw|palau)|" + + "(ri|rhode[" + SP + "]+island)|" + + "(sc|south[" + SP + "]+carolina)|" + + "(sd|south[" + SP + "]+dakota)|" + + "(tn|tennessee)|" + + "(tx|texas)|" + + "(ut|utah)|" + + "(va|virginia)|" + + "(vi|virgin[" + SP + "]+islands)|" + + "(vt|vermont)|" + + "(wa|washington)|" + + "(wi|wisconsin)|" + + "(wv|west[" + SP + "]+virginia)|" + + "(wy|wyoming)" + + ")" + WORD_END, + Pattern.CASE_INSENSITIVE); + + private static final Pattern sLocationNameRe = Pattern.compile("(?:" + + "alley|annex|arcade|ave[.]?|avenue|alameda|bayou|" + + "beach|bend|bluffs?|bottom|boulevard|branch|bridge|" + + "brooks?|burgs?|bypass|broadway|camino|camp|canyon|" + + "cape|causeway|centers?|circles?|cliffs?|club|common|" + + "corners?|course|courts?|coves?|creek|crescent|crest|" + + "crossing|crossroad|curve|circulo|dale|dam|divide|" + + "drives?|estates?|expressway|extensions?|falls?|ferry|" + + "fields?|flats?|fords?|forest|forges?|forks?|fort|" + + "freeway|gardens?|gateway|glens?|greens?|groves?|" + + "harbors?|haven|heights|highway|hills?|hollow|inlet|" + + "islands?|isle|junctions?|keys?|knolls?|lakes?|land|" + + "landing|lane|lights?|loaf|locks?|lodge|loop|mall|" + + "manors?|meadows?|mews|mills?|mission|motorway|mount|" + + "mountains?|neck|orchard|oval|overpass|parks?|" + + "parkways?|pass|passage|path|pike|pines?|plains?|" + + "plaza|points?|ports?|prairie|privada|radial|ramp|" + + "ranch|rapids?|rd[.]?|rest|ridges?|river|roads?|route|" + + "row|rue|run|shoals?|shores?|skyway|springs?|spurs?|" + + "squares?|station|stravenue|stream|st[.]?|streets?|" + + "summit|speedway|terrace|throughway|trace|track|" + + "trafficway|trail|tunnel|turnpike|underpass|unions?|" + + "valleys?|viaduct|views?|villages?|ville|vista|walks?|" + + "wall|ways?|wells?|xing|xrd)" + WORD_END, + Pattern.CASE_INSENSITIVE); + + private static final Pattern sSuffixedNumberRe = + Pattern.compile("(\\d+)(st|nd|rd|th)", Pattern.CASE_INSENSITIVE); + + private static final Pattern sZipCodeRe = + Pattern.compile("(?:\\d{5}(?:-\\d{4})?)" + WORD_END, Pattern.CASE_INSENSITIVE); + + private static boolean checkHouseNumber(String houseNumber) { + // Make sure that there are at most 5 digits. + int digitCount = 0; + for (int i = 0; i < houseNumber.length(); ++i) { + if (Character.isDigit(houseNumber.charAt(i))) ++digitCount; + } + if (digitCount > 5) return false; + + // Make sure that any ordinals are valid. + Matcher suffixMatcher = sSuffixedNumberRe.matcher(houseNumber); + while (suffixMatcher.find()) { + int num = Integer.parseInt(suffixMatcher.group(1)); + if (num == 0) { + return false; // 0th is invalid. + } + String suffix = suffixMatcher.group(2).toLowerCase(Locale.getDefault()); + switch (num % 10) { + case 1: + return suffix.equals(num % 100 == 11 ? "th" : "st"); + case 2: + return suffix.equals(num % 100 == 12 ? "th" : "nd"); + case 3: + return suffix.equals(num % 100 == 13 ? "th" : "rd"); + default: + return suffix.equals("th"); + } + } + return true; + } + + /** + * Attempt to match a house number beginnning at position offset + * in content. The house number must be followed by a word + * delimiter or the end of the string, and if offset is non-zero, + * then it must also be preceded by a word delimiter. + * + * @return a MatchResult if a valid house number was found. + */ + private static MatchResult matchHouseNumber(String content, int offset) { + if (offset > 0 && HOUSE_PRE_DELIM.indexOf(content.charAt(offset - 1)) == -1) return null; + Matcher matcher = sHouseNumberRe.matcher(content).region(offset, content.length()); + if (matcher.lookingAt()) { + MatchResult matchResult = matcher.toMatchResult(); + if (checkHouseNumber(matchResult.group(0))) return matchResult; + } + return null; + } + + /** + * Attempt to match a US state beginnning at position offset in + * content. The matching state must be followed by a word + * delimiter or the end of the string, and if offset is non-zero, + * then it must also be preceded by a word delimiter. + * + * @return a MatchResult if a valid US state (or two letter code) + * was found. + */ + private static MatchResult matchState(String content, int offset) { + if (offset > 0 && WORD_DELIM.indexOf(content.charAt(offset - 1)) == -1) return null; + Matcher stateMatcher = sStateRe.matcher(content).region(offset, content.length()); + return stateMatcher.lookingAt() ? stateMatcher.toMatchResult() : null; + } + + /** + * Test whether zipCode matches the U.S. zip code format (ddddd or + * ddddd-dddd) and is within the expected range, given that + * stateMatch is a match of sStateRe. + * + * @return true if zipCode is a valid zip code, is legal for the + * matched state, and is followed by a word delimiter or the end + * of the string. + */ + private static boolean isValidZipCode(String zipCode, MatchResult stateMatch) { + if (stateMatch == null) return false; + // Work out the index of the state, based on which group matched. + int stateIndex = stateMatch.groupCount(); + while (stateIndex > 0) { + if (stateMatch.group(stateIndex--) != null) break; + } + return sZipCodeRe.matcher(zipCode).matches() + && sStateZipCodeRanges[stateIndex].matches(zipCode); + } + + /** + * Test whether location is one of the valid locations. + * + * @return true if location starts with a valid location name + * followed by a word delimiter or the end of the string. + */ + private static boolean isValidLocationName(String location) { + return sLocationNameRe.matcher(location).matches(); + } + + /** + * Attempt to match a complete address in content, starting with + * houseNumberMatch. + * + * @param content The string to search. + * @param houseNumberMatch A matching house number to start extending. + * @return +ve: the end of the match + * +ve: the position to restart searching for house numbers, negated. + */ + private static int attemptMatch(String content, MatchResult houseNumberMatch) { + int restartPos = -1; + int nonZipMatch = -1; + int it = houseNumberMatch.end(); + int numLines = 1; + boolean consecutiveHouseNumbers = true; + boolean foundLocationName = false; + int wordCount = 1; + String lastWord = ""; + + Matcher matcher = sWordRe.matcher(content); + + for (; it < content.length(); lastWord = matcher.group(0), it = matcher.end()) { + if (!matcher.find(it)) { + // No more words in the input sequence. + return -content.length(); + } + if (matcher.end() - matcher.start() > kMaxAddressNameWordLength) { + // Word is too long to be part of an address. Fail. + return -matcher.end(); + } + + // Count the number of newlines we just consumed. + while (it < matcher.start()) { + if (NL.indexOf(content.charAt(it++)) != -1) ++numLines; + } + + // Consumed too many lines. Fail. + if (numLines > MAX_ADDRESS_LINES) break; + + // Consumed too many words. Fail. + if (++wordCount > MAX_ADDRESS_WORDS) break; + + if (matchHouseNumber(content, it) != null) { + if (consecutiveHouseNumbers && numLines > 1) { + // Last line ended with a number, and this this line starts with one. + // Restart at this number. + return -it; + } + // Remember the position of this match as the restart position. + if (restartPos == -1) restartPos = it; + continue; + } + + consecutiveHouseNumbers = false; + + if (isValidLocationName(matcher.group(0))) { + foundLocationName = true; + continue; + } + + if (wordCount == MAX_LOCATION_NAME_DISTANCE && !foundLocationName) { + // Didn't find a location name in time. Fail. + it = matcher.end(); + break; + } + + if (foundLocationName && wordCount > MIN_ADDRESS_WORDS) { + // We can now attempt to match a state. + MatchResult stateMatch = matchState(content, it); + if (stateMatch != null) { + if (lastWord.equals("et") && stateMatch.group(0).equals("al")) { + // Reject "et al" as a false postitive. + it = stateMatch.end(); + break; + } + + // At this point we've matched a state; try to match a zip code after it. + Matcher zipMatcher = sWordRe.matcher(content); + if (zipMatcher.find(stateMatch.end()) + && isValidZipCode(zipMatcher.group(0), stateMatch)) { + return zipMatcher.end(); + } + // The content ends with a state but no zip + // code. This is a legal match according to the + // documentation. N.B. This differs from the + // original c++ implementation, which only allowed + // the zip code to be optional at the end of the + // string, which presumably is a bug. Now we + // prefer to find a match with a zip code, but + // remember non-zip matches and return them if + // necessary. + nonZipMatch = stateMatch.end(); + } + } + } + + if (nonZipMatch > 0) return nonZipMatch; + + return -(restartPos > 0 ? restartPos : it); + } + + /** + * Return the first matching address in content. + * + * @param content The string to search. + * @return The first valid address, or null if no address was matched. + */ + static String findAddress(String content) { + Matcher houseNumberMatcher = sHouseNumberRe.matcher(content); + int start = 0; + while (houseNumberMatcher.find(start)) { + if (checkHouseNumber(houseNumberMatcher.group(0))) { + start = houseNumberMatcher.start(); + int end = attemptMatch(content, houseNumberMatcher); + if (end > 0) { + return content.substring(start, end); + } + start = -end; + } else { + start = houseNumberMatcher.end(); + } + } + return null; + } +} diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 5316d0db376e..deb94e816a69 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1831,9 +1831,10 @@ public class WebView extends AbsoluteLayout */ @Nullable public static String findAddress(String addr) { - // TODO: Rewrite this in Java so it is not needed to start up chromium - // Could also be deprecated - return getFactory().getStatics().findAddress(addr); + if (addr == null) { + throw new NullPointerException("addr is null"); + } + return FindAddress.findAddress(addr); } /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 73d53d4dfc8f..ff374fa90d1c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -311,7 +311,6 @@ import java.util.Locale; public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { static final String LOG_TAG = "TextView"; static final boolean DEBUG_EXTRACT = false; - static final boolean DEBUG_AUTOFILL = false; private static final float[] TEMP_POSITION = new float[2]; // Enum for the "typeface" XML parameter. @@ -789,7 +788,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Indicates whether the text was set statically or dynamically, so it can be used to // sanitize autofill requests. - private boolean mSetFromXmlOrResourceId = false; + private boolean mTextSetFromXmlOrResourceId = false; // Resource id used to set the text - used for autofill purposes. private @StringRes int mTextId = ResourceId.ID_NULL; @@ -931,7 +930,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int n = a.getIndexCount(); // Must set id in a temporary variable because it will be reset by setText() - boolean setFromXml = false; + boolean textIsSetFromXml = false; for (int i = 0; i < n; i++) { int attr = a.getIndex(i); @@ -1073,7 +1072,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_text: - setFromXml = true; + textIsSetFromXml = true; mTextId = a.getResourceId(attr, ResourceId.ID_NULL); text = a.getText(attr); break; @@ -1466,8 +1465,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } setText(text, bufferType); - if (setFromXml) { - mSetFromXmlOrResourceId = true; + if (textIsSetFromXml) { + mTextSetFromXmlOrResourceId = true; } if (hint != null) setHint(hint); @@ -5336,7 +5335,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) { - mSetFromXmlOrResourceId = false; + mTextSetFromXmlOrResourceId = false; if (text == null) { text = ""; } @@ -5574,7 +5573,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @android.view.RemotableViewMethod public final void setText(@StringRes int resid) { setText(getContext().getResources().getText(resid)); - mSetFromXmlOrResourceId = true; + mTextSetFromXmlOrResourceId = true; mTextId = resid; } @@ -5602,7 +5601,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ public final void setText(@StringRes int resid, BufferType type) { setText(getContext().getResources().getText(resid), type); - mSetFromXmlOrResourceId = true; + mTextSetFromXmlOrResourceId = true; mTextId = resid; } @@ -9516,7 +9515,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } final AutofillManager afm = mContext.getSystemService(AutofillManager.class); if (afm != null) { - if (DEBUG_AUTOFILL) { + if (android.view.autofill.Helper.sVerbose) { Log.v(LOG_TAG, "sendAfterTextChanged(): notify AFM for text=" + mText); } afm.notifyValueChanged(TextView.this); @@ -10294,9 +10293,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean isPassword = hasPasswordTransformationMethod() || isPasswordInputType(getInputType()); if (forAutofill) { - structure.setDataIsSensitive(!mSetFromXmlOrResourceId); + structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId); if (mTextId != ResourceId.ID_NULL) { - structure.setTextIdEntry(getResources().getResourceEntryName(mTextId)); + try { + structure.setTextIdEntry(getResources().getResourceEntryName(mTextId)); + } catch (Resources.NotFoundException e) { + if (android.view.autofill.Helper.sVerbose) { + Log.v(LOG_TAG, "onProvideAutofillStructure(): cannot set name for text id " + + mTextId + ": " + e.getMessage()); + } + } } } diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index efc9c02f1da9..79f4c1b1141d 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -153,7 +153,6 @@ public final class ProcessState { private int mNumActiveServices; private int mNumStartedServices; - private int mNumExcessiveWake; private int mNumExcessiveCpu; private int mNumCachedKill; @@ -470,9 +469,23 @@ public final class ProcessState { } } - public void addPss(long pss, long uss, boolean always, + public void addPss(long pss, long uss, boolean always, int type, long duration, ArrayMap<String, ProcessStateHolder> pkgList) { ensureNotDead(); + switch (type) { + case ProcessStats.ADD_PSS_INTERNAL: + mStats.mInternalPssCount++; + mStats.mInternalPssTime += duration; + break; + case ProcessStats.ADD_PSS_EXTERNAL: + mStats.mExternalPssCount++; + mStats.mExternalPssTime += duration; + break; + case ProcessStats.ADD_PSS_EXTERNAL_SLOW: + mStats.mExternalSlowPssCount++; + mStats.mExternalSlowPssTime += duration; + break; + } if (!always) { if (mLastPssState == mCurState && SystemClock.uptimeMillis() < (mLastPssTime+(30*1000))) { diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index 96ba2b0c5fd9..35b2dd23a831 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -134,6 +134,10 @@ public final class ProcessStats implements Parcelable { public static final int FLAG_SHUTDOWN = 1<<1; public static final int FLAG_SYSPROPS = 1<<2; + public static final int ADD_PSS_INTERNAL = 0; + public static final int ADD_PSS_EXTERNAL = 1; + public static final int ADD_PSS_EXTERNAL_SLOW = 2; + public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL }; @@ -158,7 +162,7 @@ public final class ProcessStats implements Parcelable { }; // Current version of the parcel format. - private static final int PARCEL_VERSION = 23; + private static final int PARCEL_VERSION = 24; // In-memory Parcel magic number, used to detect attempts to unmarshall bad data private static final int MAGIC = 0x50535454; @@ -183,6 +187,18 @@ public final class ProcessStats implements Parcelable { boolean mHasSwappedOutPss; + // Count and total time expended doing "quick" pss computations for internal use. + public long mInternalPssCount; + public long mInternalPssTime; + + // Count and total time expended doing "quick" pss computations due to external requests. + public long mExternalPssCount; + public long mExternalPssTime; + + // Count and total time expended doing full/slow pss computations due to external requests. + public long mExternalSlowPssCount; + public long mExternalSlowPssTime; + public final SparseMappingTable mTableData = new SparseMappingTable(); public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT]; @@ -302,6 +318,13 @@ public final class ProcessStats implements Parcelable { mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime; mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime; + mInternalPssCount += other.mInternalPssCount; + mInternalPssTime += other.mInternalPssTime; + mExternalPssCount += other.mExternalPssCount; + mExternalPssTime += other.mExternalPssTime; + mExternalSlowPssCount += other.mExternalSlowPssCount; + mExternalSlowPssTime += other.mExternalSlowPssTime; + mHasSwappedOutPss |= other.mHasSwappedOutPss; } @@ -500,6 +523,12 @@ public final class ProcessStats implements Parcelable { buildTimePeriodStartClockStr(); mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis(); + mInternalPssCount = 0; + mInternalPssTime = 0; + mExternalPssCount = 0; + mExternalPssTime = 0; + mExternalSlowPssCount = 0; + mExternalSlowPssTime = 0; mTableData.reset(); Arrays.fill(mMemFactorDurations, 0); mSysMemUsage.resetTable(); @@ -760,6 +789,12 @@ public final class ProcessStats implements Parcelable { out.writeLong(mTimePeriodEndRealtime); out.writeLong(mTimePeriodStartUptime); out.writeLong(mTimePeriodEndUptime); + out.writeLong(mInternalPssCount); + out.writeLong(mInternalPssTime); + out.writeLong(mExternalPssCount); + out.writeLong(mExternalPssTime); + out.writeLong(mExternalSlowPssCount); + out.writeLong(mExternalSlowPssTime); out.writeString(mRuntime); out.writeInt(mHasSwappedOutPss ? 1 : 0); out.writeInt(mFlags); @@ -928,6 +963,12 @@ public final class ProcessStats implements Parcelable { mTimePeriodEndRealtime = in.readLong(); mTimePeriodStartUptime = in.readLong(); mTimePeriodEndUptime = in.readLong(); + mInternalPssCount = in.readLong(); + mInternalPssTime = in.readLong(); + mExternalPssCount = in.readLong(); + mExternalPssTime = in.readLong(); + mExternalSlowPssCount = in.readLong(); + mExternalSlowPssTime = in.readLong(); mRuntime = in.readString(); mHasSwappedOutPss = in.readInt() != 0; mFlags = in.readInt(); @@ -1484,9 +1525,31 @@ public final class ProcessStats implements Parcelable { totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss, totalMem.processStateSamples[STATE_SERVICE_RESTARTING]); pw.println(); + pw.println("PSS collection stats:"); + pw.print(" Internal: "); + pw.print(mInternalPssCount); + pw.print("x over "); + TimeUtils.formatDuration(mInternalPssTime, pw); + pw.println(); + pw.print(" External: "); + pw.print(mExternalPssCount); + pw.print("x over "); + TimeUtils.formatDuration(mExternalPssTime, pw); + pw.println(); + pw.print(" External Slow: "); + pw.print(mExternalSlowPssCount); + pw.print("x over "); + TimeUtils.formatDuration(mExternalSlowPssTime, pw); + pw.println(); + pw.println(); pw.print(" Start time: "); pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock)); pw.println(); + pw.print(" Total uptime: "); + TimeUtils.formatDuration( + (mRunning ? SystemClock.uptimeMillis() : mTimePeriodEndUptime) + - mTimePeriodStartUptime, pw); + pw.println(); pw.print(" Total elapsed time: "); TimeUtils.formatDuration( (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime) diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index c420e6d9c3f2..b8ff9e4e3ebc 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -21,10 +21,13 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.UidTraffic; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.NetworkStats; +import android.net.Uri; import android.net.wifi.WifiActivityEnergyInfo; import android.net.wifi.WifiManager; import android.os.BatteryManager; @@ -46,6 +49,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; +import android.provider.Settings; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; import android.telephony.ServiceState; @@ -54,6 +58,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.IntArray; +import android.util.KeyValueListParser; import android.util.Log; import android.util.LogWriter; import android.util.LongSparseArray; @@ -124,7 +129,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 172 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 173 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS; @@ -289,12 +294,21 @@ public class BatteryStatsImpl extends BatteryStats { /** * Update per-freq cpu times for all the uids in {@link #mPendingUids}. */ - public void updateProcStateCpuTimes() { + public void updateProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff) { final SparseIntArray uidStates; synchronized (BatteryStatsImpl.this) { + if (!mConstants.TRACK_CPU_TIMES_BY_PROC_STATE) { + return; + } if(!initKernelSingleUidTimeReaderLocked()) { return; } + // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to + // compute deltas since it might result in mis-attributing cpu times to wrong states. + if (mKernelSingleUidTimeReader.hasStaleData()) { + mPendingUids.clear(); + return; + } if (mPendingUids.size() == 0) { return; @@ -307,7 +321,6 @@ public class BatteryStatsImpl extends BatteryStats { final int procState = uidStates.valueAt(i); final int[] isolatedUids; final Uid u; - final boolean onBattery; synchronized (BatteryStatsImpl.this) { // It's possible that uid no longer exists and any internal references have // already been deleted, so using {@link #getAvailableUidStatsLocked} to avoid @@ -324,7 +337,6 @@ public class BatteryStatsImpl extends BatteryStats { isolatedUids[j] = u.mChildUids.get(j); } } - onBattery = mOnBatteryInternal; } long[] cpuTimesMs = mKernelSingleUidTimeReader.readDeltaMs(uid); if (isolatedUids != null) { @@ -335,27 +347,45 @@ public class BatteryStatsImpl extends BatteryStats { } if (onBattery && cpuTimesMs != null) { synchronized (BatteryStatsImpl.this) { - u.addProcStateTimesMs(procState, cpuTimesMs); - u.addProcStateScreenOffTimesMs(procState, cpuTimesMs); + u.addProcStateTimesMs(procState, cpuTimesMs, onBattery); + u.addProcStateScreenOffTimesMs(procState, cpuTimesMs, onBatteryScreenOff); } } } } + public void copyFromAllUidsCpuTimes() { + synchronized (BatteryStatsImpl.this) { + copyFromAllUidsCpuTimes( + mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning()); + } + } + /** * When the battery/screen state changes, we don't attribute the cpu times to any process * but we still need to snapshots of all uids to get correct deltas later on. Since we * already read this data for updating per-freq cpu times, we can use the same data for * per-procstate cpu times. */ - public void copyFromAllUidsCpuTimes() { + public void copyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff) { synchronized (BatteryStatsImpl.this) { + if (!mConstants.TRACK_CPU_TIMES_BY_PROC_STATE) { + return; + } if(!initKernelSingleUidTimeReaderLocked()) { return; } final SparseArray<long[]> allUidCpuFreqTimesMs = mKernelUidCpuFreqTimeReader.getAllUidCpuFreqTimeMs(); + // If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to + // compute deltas since it might result in mis-attributing cpu times to wrong states. + if (mKernelSingleUidTimeReader.hasStaleData()) { + mKernelSingleUidTimeReader.setAllUidsCpuTimesMs(allUidCpuFreqTimesMs); + mKernelSingleUidTimeReader.markDataAsStale(false); + mPendingUids.clear(); + return; + } for (int i = allUidCpuFreqTimesMs.size() - 1; i >= 0; --i) { final int uid = allUidCpuFreqTimesMs.keyAt(i); final Uid u = getAvailableUidStatsLocked(mapUid(uid)); @@ -368,7 +398,7 @@ public class BatteryStatsImpl extends BatteryStats { } final long[] deltaTimesMs = mKernelSingleUidTimeReader.computeDelta( uid, cpuTimesMs.clone()); - if (mOnBatteryInternal && deltaTimesMs != null) { + if (onBattery && deltaTimesMs != null) { final int procState; final int idx = mPendingUids.indexOfKey(uid); if (idx >= 0) { @@ -378,8 +408,8 @@ public class BatteryStatsImpl extends BatteryStats { procState = u.mProcessState; } if (procState >= 0 && procState < Uid.NUM_PROCESS_STATE) { - u.addProcStateTimesMs(procState, deltaTimesMs); - u.addProcStateScreenOffTimesMs(procState, deltaTimesMs); + u.addProcStateTimesMs(procState, deltaTimesMs, onBattery); + u.addProcStateScreenOffTimesMs(procState, deltaTimesMs, onBatteryScreenOff); } } } @@ -443,8 +473,9 @@ public class BatteryStatsImpl extends BatteryStats { Future<?> scheduleSync(String reason, int flags); Future<?> scheduleCpuSyncDueToRemovedUid(int uid); - Future<?> scheduleReadProcStateCpuTimes(); - Future<?> scheduleCopyFromAllUidsCpuTimes(); + Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff); + Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff); + Future<?> scheduleCpuSyncDueToSettingChange(); } public Handler mHandler; @@ -807,6 +838,9 @@ public class BatteryStatsImpl extends BatteryStats { @VisibleForTesting protected PowerProfile mPowerProfile; + @GuardedBy("this") + private final Constants mConstants; + /* * Holds a SamplingTimer associated with each Resource Power Manager state and voter, * recording their times when on-battery (regardless of screen state). @@ -895,6 +929,7 @@ public class BatteryStatsImpl extends BatteryStats { mHandler = null; mPlatformIdleStateCallback = null; mUserInfoProvider = null; + mConstants = new Constants(mHandler); clearHistoryLocked(); } @@ -1231,12 +1266,10 @@ public class BatteryStatsImpl extends BatteryStats { public long[] mCounts; public long[] mLoadedCounts; public long[] mUnpluggedCounts; - public long[] mPluggedCounts; private LongSamplingCounterArray(TimeBase timeBase, Parcel in) { mTimeBase = timeBase; - mPluggedCounts = in.createLongArray(); - mCounts = copyArray(mPluggedCounts, mCounts); + mCounts = in.createLongArray(); mLoadedCounts = in.createLongArray(); mUnpluggedCounts = in.createLongArray(); timeBase.add(this); @@ -1255,17 +1288,16 @@ public class BatteryStatsImpl extends BatteryStats { @Override public void onTimeStarted(long elapsedRealTime, long baseUptime, long baseRealtime) { - mUnpluggedCounts = copyArray(mPluggedCounts, mUnpluggedCounts); + mUnpluggedCounts = copyArray(mCounts, mUnpluggedCounts); } @Override public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) { - mPluggedCounts = copyArray(mCounts, mPluggedCounts); } @Override public long[] getCountsLocked(int which) { - long[] val = copyArray(mTimeBase.isRunning() ? mCounts : mPluggedCounts, null); + long[] val = copyArray(mCounts, null); if (which == STATS_SINCE_UNPLUGGED) { subtract(val, mUnpluggedCounts); } else if (which != STATS_SINCE_CHARGED) { @@ -1278,15 +1310,18 @@ public class BatteryStatsImpl extends BatteryStats { public void logState(Printer pw, String prefix) { pw.println(prefix + "mCounts=" + Arrays.toString(mCounts) + " mLoadedCounts=" + Arrays.toString(mLoadedCounts) - + " mUnpluggedCounts=" + Arrays.toString(mUnpluggedCounts) - + " mPluggedCounts=" + Arrays.toString(mPluggedCounts)); + + " mUnpluggedCounts=" + Arrays.toString(mUnpluggedCounts)); } public void addCountLocked(long[] counts) { + addCountLocked(counts, mTimeBase.isRunning()); + } + + public void addCountLocked(long[] counts, boolean isRunning) { if (counts == null) { return; } - if (mTimeBase.isRunning()) { + if (isRunning) { if (mCounts == null) { mCounts = new long[counts.length]; } @@ -1306,7 +1341,6 @@ public class BatteryStatsImpl extends BatteryStats { public void reset(boolean detachIfReset) { fillArray(mCounts, 0); fillArray(mLoadedCounts, 0); - fillArray(mPluggedCounts, 0); fillArray(mUnpluggedCounts, 0); if (detachIfReset) { detach(); @@ -1325,7 +1359,6 @@ public class BatteryStatsImpl extends BatteryStats { mCounts = in.createLongArray(); mLoadedCounts = copyArray(mCounts, mLoadedCounts); mUnpluggedCounts = copyArray(mCounts, mUnpluggedCounts); - mPluggedCounts = copyArray(mCounts, mPluggedCounts); } public static void writeToParcel(Parcel out, LongSamplingCounterArray counterArray) { @@ -3783,7 +3816,8 @@ public class BatteryStatsImpl extends BatteryStats { + " and battery is " + (unplugged ? "on" : "off")); } updateCpuTimeLocked(); - mExternalSync.scheduleCopyFromAllUidsCpuTimes(); + mExternalSync.scheduleCopyFromAllUidsCpuTimes(mOnBatteryTimeBase.isRunning(), + mOnBatteryScreenOffTimeBase.isRunning()); mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime); if (updateOnBatteryTimeBase) { @@ -6648,7 +6682,7 @@ public class BatteryStatsImpl extends BatteryStats { return null; } - private void addProcStateTimesMs(int procState, long[] cpuTimesMs) { + private void addProcStateTimesMs(int procState, long[] cpuTimesMs, boolean onBattery) { if (mProcStateTimeMs == null) { mProcStateTimeMs = new LongSamplingCounterArray[NUM_PROCESS_STATE]; } @@ -6657,10 +6691,11 @@ public class BatteryStatsImpl extends BatteryStats { mProcStateTimeMs[procState] = new LongSamplingCounterArray( mBsi.mOnBatteryTimeBase); } - mProcStateTimeMs[procState].addCountLocked(cpuTimesMs); + mProcStateTimeMs[procState].addCountLocked(cpuTimesMs, onBattery); } - private void addProcStateScreenOffTimesMs(int procState, long[] cpuTimesMs) { + private void addProcStateScreenOffTimesMs(int procState, long[] cpuTimesMs, + boolean onBatteryScreenOff) { if (mProcStateScreenOffTimeMs == null) { mProcStateScreenOffTimeMs = new LongSamplingCounterArray[NUM_PROCESS_STATE]; } @@ -6669,7 +6704,7 @@ public class BatteryStatsImpl extends BatteryStats { mProcStateScreenOffTimeMs[procState] = new LongSamplingCounterArray( mBsi.mOnBatteryScreenOffTimeBase); } - mProcStateScreenOffTimeMs[procState].addCountLocked(cpuTimesMs); + mProcStateScreenOffTimeMs[procState].addCountLocked(cpuTimesMs, onBatteryScreenOff); } @Override @@ -9406,9 +9441,11 @@ public class BatteryStatsImpl extends BatteryStats { if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) { mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs); - if (mBsi.mPerProcStateCpuTimesAvailable) { + if (mBsi.trackPerProcStateCpuTimes()) { if (mBsi.mPendingUids.size() == 0) { - mBsi.mExternalSync.scheduleReadProcStateCpuTimes(); + mBsi.mExternalSync.scheduleReadProcStateCpuTimes( + mBsi.mOnBatteryTimeBase.isRunning(), + mBsi.mOnBatteryScreenOffTimeBase.isRunning()); } if (mBsi.mPendingUids.indexOfKey(mUid) < 0 || ArrayUtils.contains(CRITICAL_PROC_STATES, mProcessState)) { @@ -9758,6 +9795,7 @@ public class BatteryStatsImpl extends BatteryStats { mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin")); mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml")); mHandler = new MyHandler(handler.getLooper()); + mConstants = new Constants(mHandler); mStartCount++; mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase); mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase); @@ -9853,6 +9891,7 @@ public class BatteryStatsImpl extends BatteryStats { mDailyFile = null; mHandler = null; mExternalSync = null; + mConstants = new Constants(mHandler); clearHistoryLocked(); readFromParcel(p); mPlatformIdleStateCallback = null; @@ -12606,6 +12645,78 @@ public class BatteryStatsImpl extends BatteryStats { mShuttingDown = true; } + public boolean trackPerProcStateCpuTimes() { + return mConstants.TRACK_CPU_TIMES_BY_PROC_STATE && mPerProcStateCpuTimesAvailable; + } + + public void systemServicesReady(Context context) { + mConstants.startObserving(context.getContentResolver()); + } + + @VisibleForTesting + public final class Constants extends ContentObserver { + public static final String KEY_TRACK_CPU_TIMES_BY_PROC_STATE + = "track_cpu_times_by_proc_state"; + + private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true; + + public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE; + + private ContentResolver mResolver; + private final KeyValueListParser mParser = new KeyValueListParser(','); + + public Constants(Handler handler) { + super(handler); + } + + public void startObserving(ContentResolver resolver) { + mResolver = resolver; + mResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.BATTERY_STATS_CONSTANTS), + false /* notifyForDescendants */, this); + updateConstants(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + updateConstants(); + } + + private void updateConstants() { + synchronized (BatteryStatsImpl.this) { + try { + mParser.setString(Settings.Global.getString(mResolver, + Settings.Global.BATTERY_STATS_CONSTANTS)); + } catch (IllegalArgumentException e) { + // Failed to parse the settings string, log this and move on + // with defaults. + Slog.e(TAG, "Bad batterystats settings", e); + } + + updateTrackCpuTimesByProcStateLocked(TRACK_CPU_TIMES_BY_PROC_STATE, + mParser.getBoolean(KEY_TRACK_CPU_TIMES_BY_PROC_STATE, + DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE)); + } + } + + private void updateTrackCpuTimesByProcStateLocked(boolean wasEnabled, boolean isEnabled) { + TRACK_CPU_TIMES_BY_PROC_STATE = isEnabled; + if (isEnabled && !wasEnabled) { + mKernelSingleUidTimeReader.markDataAsStale(true); + mExternalSync.scheduleCpuSyncDueToSettingChange(); + } + } + + public void dumpLocked(PrintWriter pw) { + pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("="); + pw.println(TRACK_CPU_TIMES_BY_PROC_STATE); + } + } + + public void dumpConstantsLocked(PrintWriter pw) { + mConstants.dumpLocked(pw); + } + Parcel mPendingWrite = null; final ReentrantLock mWriteLock = new ReentrantLock(); diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java index ca635a409a44..ebeb24c41479 100644 --- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java @@ -46,12 +46,14 @@ public class KernelSingleUidTimeReader { private final int mCpuFreqsCount; @GuardedBy("this") - private final SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>(); + private SparseArray<long[]> mLastUidCpuTimeMs = new SparseArray<>(); @GuardedBy("this") private int mReadErrorCounter; @GuardedBy("this") private boolean mSingleUidCpuTimesAvailable = true; + @GuardedBy("this") + private boolean mHasStaleData; private final Injector mInjector; @@ -166,6 +168,30 @@ public class KernelSingleUidTimeReader { return deltaTimesMs; } + public void markDataAsStale(boolean hasStaleData) { + synchronized (this) { + mHasStaleData = hasStaleData; + } + } + + public boolean hasStaleData() { + synchronized (this) { + return mHasStaleData; + } + } + + public void setAllUidsCpuTimesMs(SparseArray<long[]> allUidsCpuTimesMs) { + synchronized (this) { + mLastUidCpuTimeMs.clear(); + for (int i = allUidsCpuTimesMs.size() - 1; i >= 0; --i) { + final long[] cpuTimesMs = allUidsCpuTimesMs.valueAt(i); + if (cpuTimesMs != null) { + mLastUidCpuTimeMs.put(allUidsCpuTimesMs.keyAt(i), cpuTimesMs.clone()); + } + } + } + } + public void removeUid(int uid) { synchronized (this) { mLastUidCpuTimeMs.delete(uid); diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index de2b7c3db11d..4257c981be18 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -12,21 +12,62 @@ static jmethodID gInputStream_readMethodID; static jmethodID gInputStream_skipMethodID; +// FIXME: Share with ByteBufferStreamAdaptor.cpp? +static JNIEnv* get_env_or_die(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); + } + return env; +} + /** * Wrapper for a Java InputStream. */ class JavaInputStreamAdaptor : public SkStream { + JavaInputStreamAdaptor(JavaVM* jvm, jobject js, jbyteArray ar, jint capacity, + bool swallowExceptions) + : fJvm(jvm) + , fJavaInputStream(js) + , fJavaByteArray(ar) + , fCapacity(capacity) + , fBytesRead(0) + , fIsAtEnd(false) + , fSwallowExceptions(swallowExceptions) {} + public: - JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar) - : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) { - SkASSERT(ar); - fCapacity = env->GetArrayLength(ar); - SkASSERT(fCapacity > 0); - fBytesRead = 0; - fIsAtEnd = false; + static JavaInputStreamAdaptor* Create(JNIEnv* env, jobject js, jbyteArray ar, + bool swallowExceptions) { + JavaVM* jvm; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); + + js = env->NewGlobalRef(js); + if (!js) { + return nullptr; + } + + ar = (jbyteArray) env->NewGlobalRef(ar); + if (!ar) { + env->DeleteGlobalRef(js); + return nullptr; + } + + jint capacity = env->GetArrayLength(ar); + return new JavaInputStreamAdaptor(jvm, js, ar, capacity, swallowExceptions); + } + + ~JavaInputStreamAdaptor() override { + auto* env = get_env_or_die(fJvm); + env->DeleteGlobalRef(fJavaInputStream); + env->DeleteGlobalRef(fJavaByteArray); } - virtual size_t read(void* buffer, size_t size) { + size_t read(void* buffer, size_t size) override { + auto* env = get_env_or_die(fJvm); + if (!fSwallowExceptions && checkException(env)) { + // Just in case the caller did not clear from a previous exception. + return 0; + } if (NULL == buffer) { if (0 == size) { return 0; @@ -37,10 +78,10 @@ public: */ size_t amountSkipped = 0; do { - size_t amount = this->doSkip(size - amountSkipped); + size_t amount = this->doSkip(size - amountSkipped, env); if (0 == amount) { char tmp; - amount = this->doRead(&tmp, 1); + amount = this->doRead(&tmp, 1, env); if (0 == amount) { // if read returned 0, we're at EOF fIsAtEnd = true; @@ -52,16 +93,13 @@ public: return amountSkipped; } } - return this->doRead(buffer, size); + return this->doRead(buffer, size, env); } - virtual bool isAtEnd() const { - return fIsAtEnd; - } + bool isAtEnd() const override { return fIsAtEnd; } private: - size_t doRead(void* buffer, size_t size) { - JNIEnv* env = fEnv; + size_t doRead(void* buffer, size_t size, JNIEnv* env) { size_t bytesRead = 0; // read the bytes do { @@ -76,13 +114,9 @@ private: jint n = env->CallIntMethod(fJavaInputStream, gInputStream_readMethodID, fJavaByteArray, 0, requested); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); + if (checkException(env)) { SkDebugf("---- read threw an exception\n"); - // Consider the stream to be at the end, since there was an error. - fIsAtEnd = true; - return 0; + return bytesRead; } if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. @@ -92,14 +126,9 @@ private: env->GetByteArrayRegion(fJavaByteArray, 0, n, reinterpret_cast<jbyte*>(buffer)); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); + if (checkException(env)) { SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); - // The error was not with the stream itself, but consider it to be at the - // end, since we do not have a way to recover. - fIsAtEnd = true; - return 0; + return bytesRead; } buffer = (void*)((char*)buffer + n); @@ -111,14 +140,10 @@ private: return bytesRead; } - size_t doSkip(size_t size) { - JNIEnv* env = fEnv; - + size_t doSkip(size_t size, JNIEnv* env) { jlong skipped = env->CallLongMethod(fJavaInputStream, gInputStream_skipMethodID, (jlong)size); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); + if (checkException(env)) { SkDebugf("------- skip threw an exception\n"); return 0; } @@ -129,20 +154,37 @@ private: return (size_t)skipped; } - JNIEnv* fEnv; - jobject fJavaInputStream; // the caller owns this object - jbyteArray fJavaByteArray; // the caller owns this object - jint fCapacity; + bool checkException(JNIEnv* env) { + if (!env->ExceptionCheck()) { + return false; + } + + env->ExceptionDescribe(); + if (fSwallowExceptions) { + env->ExceptionClear(); + } + + // There is no way to recover from the error, so consider the stream + // to be at the end. + fIsAtEnd = true; + + return true; + } + + JavaVM* fJvm; + jobject fJavaInputStream; + jbyteArray fJavaByteArray; + const jint fCapacity; size_t fBytesRead; bool fIsAtEnd; + const bool fSwallowExceptions; }; -SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, - jbyteArray storage) { - return new JavaInputStreamAdaptor(env, stream, storage); +SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, + bool swallowExceptions) { + return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions); } - static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { SkASSERT(stream != NULL); size_t bufferSize = 4096; diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h index 56cba51222a0..fccd4717c4b7 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h @@ -16,13 +16,16 @@ class SkWStream; * @param stream Pointer to Java InputStream. * @param storage Java byte array for retrieving data from the * Java InputStream. + * @param swallowExceptions Whether to call ExceptionClear() after + * an Exception is thrown. If false, it is up to the client to + * clear or propagate the exception. * @return SkStream Simple subclass of SkStream which supports its * basic methods like reading. Only valid until the calling * function returns, since the Java InputStream is not managed * by the SkStream. */ -SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, - jbyteArray storage); +SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, + bool swallowExceptions = true); /** * Copy a Java InputStream. The result will be rewindable. @@ -33,10 +36,8 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, * @return SkStreamRewindable The data in stream will be copied * to a new SkStreamRewindable. */ -SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, - jbyteArray storage); +SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); -SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, - jbyteArray storage); +SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); #endif // _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index bacab2a304cc..5bdad08e3e1e 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -16,6 +16,7 @@ #include "Bitmap.h" #include "ByteBufferStreamAdaptor.h" +#include "CreateJavaOutputStreamAdaptor.h" #include "GraphicsJNI.h" #include "NinePatchPeeker.h" #include "Utils.h" @@ -26,10 +27,12 @@ #include <SkAndroidCodec.h> #include <SkEncodedImageFormat.h> +#include <SkFrontBufferedStream.h> #include <SkStream.h> #include <androidfw/Asset.h> #include <jni.h> +#include <sys/stat.h> using namespace android; @@ -69,15 +72,15 @@ struct ImageDecoder { static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { if (!stream.get()) { - return nullObjectReturn("Failed to create a stream"); + doThrowIOE(env, "Failed to create a stream"); + return nullptr; } std::unique_ptr<ImageDecoder> decoder(new ImageDecoder); decoder->mCodec = SkAndroidCodec::MakeFromStream(std::move(stream), &decoder->mPeeker); if (!decoder->mCodec.get()) { - // FIXME: Add an error code to SkAndroidCodec::MakeFromStream, like - // SkCodec? Then this can print a more informative error message. - // (Or we can print one from within SkCodec.) - ALOGE("Failed to create an SkCodec"); + // FIXME: (b/71578461) Use the error message from + // SkCodec::MakeFromStream to report a more informative error message. + doThrowIOE(env, "Failed to create an SkCodec"); return nullptr; } @@ -88,7 +91,52 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { reinterpret_cast<jlong>(decoder.release()), width, height); } -static jobject ImageDecoder_nCreate(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) { +static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, + jobject fileDescriptor) { + int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); + + struct stat fdStat; + if (fstat(descriptor, &fdStat) == -1) { + doThrowIOE(env, "broken file descriptor; fstat returned -1"); + return nullptr; + } + + int dupDescriptor = dup(descriptor); + FILE* file = fdopen(dupDescriptor, "r"); + if (file == NULL) { + close(dupDescriptor); + doThrowIOE(env, "Could not open file"); + return nullptr; + } + std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); + + if (::lseek(descriptor, 0, SEEK_CUR) == 0) { + return native_create(env, std::move(fileStream)); + } + + // FIXME: This allows us to pretend the current location is the beginning, + // but it would be better if SkFILEStream allowed treating its starting + // point as the beginning. + std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream), + SkCodec::MinBufferedBytesNeeded())); + return native_create(env, std::move(stream)); +} + +static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, + jobject is, jbyteArray storage) { + std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false)); + + if (!stream.get()) { + doThrowIOE(env, "Failed to create stream!"); + return nullptr; + } + std::unique_ptr<SkStream> bufferedStream( + SkFrontBufferedStream::Make(std::move(stream), + SkCodec::MinBufferedBytesNeeded())); + return native_create(env, std::move(bufferedStream)); +} + +static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) { Asset* asset = reinterpret_cast<Asset*>(assetPtr); std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset)); return native_create(env, std::move(stream)); @@ -99,6 +147,7 @@ static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jo std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer, initialPosition, limit); if (!stream) { + doThrowIOE(env, "Failed to read ByteBuffer"); return nullptr; } return native_create(env, std::move(stream)); @@ -114,6 +163,7 @@ static bool supports_any_down_scale(const SkAndroidCodec* codec) { return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP; } +// This method should never return null. Instead, it should throw an exception. static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jcallback, jobject jpostProcess, jint desiredWidth, jint desiredHeight, jobject jsubset, @@ -165,7 +215,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong case kOpaque_SkAlphaType: break; case kUnknown_SkAlphaType: - return nullObjectReturn("Unknown alpha type"); + doThrowIOE(env, "Unknown alpha type"); + return nullptr; } SkColorType colorType = kN32_SkColorType; @@ -200,7 +251,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); } if (!bm.setInfo(bitmapInfo)) { - return nullObjectReturn("Failed to setInfo properly"); + doThrowIOE(env, "Failed to setInfo properly"); + return nullptr; } sk_sp<Bitmap> nativeBitmap; @@ -213,35 +265,44 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativeBitmap = Bitmap::allocateHeapBitmap(&bm); } if (!nativeBitmap) { - ALOGE("OOM allocating Bitmap with dimensions %i x %i", - decodeInfo.width(), decodeInfo.height()); - doThrowOOME(env); + SkString msg; + msg.printf("OOM allocating Bitmap with dimensions %i x %i", + decodeInfo.width(), decodeInfo.height()); + doThrowOOME(env, msg.c_str()); return nullptr; } - jobject jexception = nullptr; SkAndroidCodec::AndroidOptions options; options.fSampleSize = sampleSize; auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options); + jobject jexception = env->ExceptionOccurred(); + if (jexception) { + env->ExceptionClear(); + } switch (result) { case SkCodec::kSuccess: + // Ignore the exception, since the decode was successful anyway. + jexception = nullptr; break; case SkCodec::kIncompleteInput: - if (jcallback) { + if (jcallback && !jexception) { jexception = env->NewObject(gIncomplete_class, gIncomplete_constructorMethodID); } break; case SkCodec::kErrorInInput: - if (jcallback) { + if (jcallback && !jexception) { jexception = env->NewObject(gCorrupt_class, gCorrupt_constructorMethodID); } break; default: - ALOGE("getPixels failed with error %i", result); + SkString msg; + msg.printf("getPixels failed with error %i", result); + doThrowIOE(env, msg.c_str()); return nullptr; } if (jexception) { + // FIXME: Do not provide a way for the client to force the method to return null. if (!env->CallBooleanMethod(jcallback, gCallback_onExceptionMethodID, jexception) || env->ExceptionCheck()) { return nullptr; @@ -268,7 +329,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong size_t ninePatchArraySize = decoder->mPeeker.mPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (ninePatchChunk == nullptr) { - return nullObjectReturn("ninePatchChunk == null"); + doThrowOOME(env, "Failed to allocate nine patch chunk."); + return nullptr; } env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker.mPatchSize, @@ -278,7 +340,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong if (decoder->mPeeker.mHasInsets) { ninePatchInsets = decoder->mPeeker.createNinePatchInsets(env, 1.0f); if (ninePatchInsets == nullptr) { - return nullObjectReturn("nine patch insets == null"); + doThrowOOME(env, "Failed to allocate nine patch insets."); + return nullptr; } } } @@ -303,7 +366,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong SkImageInfo scaledInfo = bitmapInfo.makeWH(desiredWidth, desiredHeight); SkBitmap scaledBm; if (!scaledBm.setInfo(scaledInfo)) { - nullObjectReturn("Failed scaled setInfo"); + doThrowIOE(env, "Failed scaled setInfo"); + return nullptr; } sk_sp<Bitmap> scaledPixelRef; @@ -313,9 +377,10 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong scaledPixelRef = Bitmap::allocateHeapBitmap(&scaledBm); } if (!scaledPixelRef) { - ALOGE("OOM allocating scaled Bitmap with dimensions %i x %i", - desiredWidth, desiredHeight); - doThrowOOME(env); + SkString msg; + msg.printf("OOM allocating scaled Bitmap with dimensions %i x %i", + desiredWidth, desiredHeight); + doThrowOOME(env, msg.c_str()); return nullptr; } @@ -334,13 +399,11 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong if (jpostProcess) { std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm)); - if (!canvas) { - return nullObjectReturn("Failed to create Canvas for PostProcess!"); - } jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID, reinterpret_cast<jlong>(canvas.get())); if (!jcanvas) { - return nullObjectReturn("Failed to create Java Canvas for PostProcess!"); + doThrowOOME(env, "Failed to create Java Canvas for PostProcess!"); + return nullptr; } // jcanvas will now own canvas. canvas.release(); @@ -368,15 +431,17 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong newAlphaType = kOpaque_SkAlphaType; break; default: - ALOGE("invalid return from postProcess: %i", pixelFormat); - doThrowIAE(env); + SkString msg; + msg.printf("invalid return from postProcess: %i", pixelFormat); + doThrowIAE(env, msg.c_str()); return nullptr; } if (newAlphaType != bm.alphaType()) { if (!bm.setAlphaType(newAlphaType)) { - ALOGE("incompatible return from postProcess: %i", pixelFormat); - doThrowIAE(env); + SkString msg; + msg.printf("incompatible return from postProcess: %i", pixelFormat); + doThrowIAE(env, msg.c_str()); return nullptr; } nativeBitmap->setAlphaType(newAlphaType); @@ -405,7 +470,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong ninePatchChunk, ninePatchInsets); } if (allocator == ImageDecoder::kHardware_Allocator) { - return nullObjectReturn("failed to allocate hardware Bitmap!"); + doThrowOOME(env, "failed to allocate hardware Bitmap!"); + return nullptr; } // If we failed to create a hardware bitmap, go ahead and create a // software one. @@ -430,19 +496,21 @@ static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativ decoder->mPeeker.getPadding(env, outPadding); } -static void ImageDecoder_nRecycle(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { +static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { delete reinterpret_cast<ImageDecoder*>(nativePtr); } static const JNINativeMethod gImageDecoderMethods[] = { - { "nCreate", "(J)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreate }, + { "nCreate", "(J)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset }, { "nCreate", "(Ljava/nio/ByteBuffer;II)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, { "nCreate", "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, + { "nCreate", "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, + { "nCreate", "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder$OnExceptionListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/graphics/Point;", (void*) ImageDecoder_nGetSampledSize }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, - { "nRecycle", "(J)V", (void*) ImageDecoder_nRecycle}, + { "nClose", "(J)V", (void*) ImageDecoder_nClose}, }; int register_android_graphics_ImageDecoder(JNIEnv* env) { @@ -459,7 +527,7 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) { gCorrupt_constructorMethodID = GetMethodIDOrDie(env, gCorrupt_class, "<init>", "()V"); jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnExceptionListener"); - gCallback_onExceptionMethodID = GetMethodIDOrDie(env, callback_class, "onException", "(Ljava/lang/Exception;)Z"); + gCallback_onExceptionMethodID = GetMethodIDOrDie(env, callback_class, "onException", "(Ljava/io/IOException;)Z"); jclass postProcess_class = FindClassOrDie(env, "android/graphics/PostProcess"); gPostProcess_postProcessMethodID = GetMethodIDOrDie(env, postProcess_class, "postProcess", "(Landroid/graphics/Canvas;II)I"); diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto index 73850509dfa8..3412a32e7b75 100644 --- a/core/proto/android/app/activitymanager.proto +++ b/core/proto/android/app/activitymanager.proto @@ -38,29 +38,29 @@ enum ProcessState { PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 300; // Process is hosting a foreground service. PROCESS_STATE_FOREGROUND_SERVICE = 400; - // Same as PROCESS_STATE_TOP but while device is sleeping. - PROCESS_STATE_TOP_SLEEPING = 500; // Process is important to the user, and something they are aware of. - PROCESS_STATE_IMPORTANT_FOREGROUND = 600; + PROCESS_STATE_IMPORTANT_FOREGROUND = 500; // Process is important to the user, but not something they are aware of. - PROCESS_STATE_IMPORTANT_BACKGROUND = 700; + PROCESS_STATE_IMPORTANT_BACKGROUND = 600; // Process is in the background transient so we will try to keep running. - PROCESS_STATE_TRANSIENT_BACKGROUND = 800; + PROCESS_STATE_TRANSIENT_BACKGROUND = 700; // Process is in the background running a backup/restore operation. - PROCESS_STATE_BACKUP = 900; - // Process is in the background, but it can't restore its state so we want - // to try to avoid killing it. - PROCESS_STATE_HEAVY_WEIGHT = 1000; + PROCESS_STATE_BACKUP = 800; // Process is in the background running a service. Unlike oom_adj, this // level is used for both the normal running in background state and the // executing operations state. - PROCESS_STATE_SERVICE = 1100; + PROCESS_STATE_SERVICE = 900; // Process is in the background running a receiver. Note that from the // perspective of oom_adj, receivers run at a higher foreground level, but // for our prioritization here that is not necessary and putting them // below services means many fewer changes in some process states as they // receive broadcasts. - PROCESS_STATE_RECEIVER = 1200; + PROCESS_STATE_RECEIVER = 1000; + // Same as PROCESS_STATE_TOP but while device is sleeping. + PROCESS_STATE_TOP_SLEEPING = 1100; + // Process is in the background, but it can't restore its state so we want + // to try to avoid killing it. + PROCESS_STATE_HEAVY_WEIGHT = 1200; // Process is in the background but hosts the home activity. PROCESS_STATE_HOME = 1300; // Process is in the background but hosts the last shown activity. @@ -70,9 +70,12 @@ enum ProcessState { // Process is being cached for later use and is a client of another cached // process that contains activities. PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1600; + // Process is being cached for later use and has an activity that corresponds + // to an existing recent task. + PROCESS_STATE_CACHED_RECENT = 1700; // Process is being cached for later use and is empty. - PROCESS_STATE_CACHED_EMPTY = 1700; + PROCESS_STATE_CACHED_EMPTY = 1800; // Process does not exist. - PROCESS_STATE_NONEXISTENT = 1800; + PROCESS_STATE_NONEXISTENT = 1900; } diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index a6db31f67b33..2f856ab68768 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -43,6 +43,8 @@ import "frameworks/base/core/proto/android/service/notification.proto"; import "frameworks/base/core/proto/android/service/package.proto"; import "frameworks/base/core/proto/android/service/print.proto"; import "frameworks/base/core/proto/android/service/procstats.proto"; +import "frameworks/base/core/proto/android/util/event_log_tags.proto"; +import "frameworks/base/core/proto/android/util/log.proto"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; import "frameworks/base/libs/incident/proto/android/section.proto"; @@ -59,6 +61,8 @@ message IncidentMetadata { // the sections are able to be controlled and configured by section ids. // Instead privacy field options need to be configured in each section proto message. message IncidentProto { + reserved 1001; + // Incident header from callers repeated IncidentHeaderProto header = 1; // Internal metadata of incidentd @@ -70,6 +74,52 @@ message IncidentProto { (section).args = "getprop" ]; + // Device Logs + optional android.util.EventLogTagMapProto event_log_tag_map = 1100 [ + (section).type = SECTION_FILE, + (section).args = "/system/etc/event-log-tags" + ]; + + optional android.util.LogProto main_logs = 1101 [ + (section).type = SECTION_LOG, + (section).args = "LOG_ID_MAIN" + ]; + + optional android.util.LogProto radio_logs = 1102 [ + (section).type = SECTION_LOG, + (section).args = "LOG_ID_RADIO" + ]; + + optional android.util.LogProto events_logs = 1103 [ + (section).type = SECTION_LOG, + (section).args = "LOG_ID_EVENTS" + ]; + + optional android.util.LogProto system_logs = 1104 [ + (section).type = SECTION_LOG, + (section).args = "LOG_ID_SYSTEM" + ]; + + optional android.util.LogProto crash_logs = 1105 [ + (section).type = SECTION_LOG, + (section).args = "LOG_ID_CRASH" + ]; + + optional android.util.LogProto stats_logs = 1106 [ + (section).type = SECTION_LOG, + (section).args = "LOG_ID_STATS" + ]; + + optional android.util.LogProto security_logs = 1107 [ + (section).type = SECTION_LOG, + (section).args = "LOG_ID_SECURITY" + ]; + + optional android.util.LogProto kernel_logs = 1108 [ + (section).type = SECTION_LOG, + (section).args = "LOG_ID_KERNEL" + ]; + // Linux services optional Procrank procrank = 2000 [ (section).type = SECTION_COMMAND, @@ -196,4 +246,5 @@ message IncidentProto { (section).type = SECTION_DUMPSYS, (section).args = "graphicsstats --proto" ]; + } diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index fb0ebed1545d..f5d098cc7771 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -582,7 +582,7 @@ message SecureSettingsProto { optional SettingProto downloads_backup_charging_only = 162; optional SettingProto automatic_storage_manager_downloads_days_to_retain = 163; optional SettingProto qs_tiles = 164; - optional SettingProto demo_user_setup_complete = 165; + reserved 165; // Removed demo_user_setup_complete optional SettingProto instant_apps_enabled = 166; optional SettingProto device_paired = 167; optional SettingProto package_verifier_state = 191; diff --git a/core/proto/android/util/event_log_tags.proto b/core/proto/android/util/event_log_tags.proto new file mode 100644 index 000000000000..cb039be55b85 --- /dev/null +++ b/core/proto/android/util/event_log_tags.proto @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.util; + +option java_multiple_files = true; + +// Proto representation of event.logtags. +// Usually sit in /system/etc/event-log-tags. +message EventLogTagMapProto { + repeated EventLogTag event_log_tags = 1; +} + +message EventLogTag { + optional uint32 tag_number = 1; // keyed by tag number. + optional string tag_name = 2; + + message ValueDescriptor { + optional string name = 1; + + enum DataType { + UNKNOWN = 0; + INT = 1; + LONG = 2; + STRING = 3; + LIST = 4; + FLOAT = 5; + } + optional DataType type = 2; + + enum DataUnit { + UNSET = 0; // this field is optional, so default is unset + OBJECTS = 1; // Number of objects + BYTES = 2; // Number of bytes (default for type of int/long) + MILLISECONDS = 3; // Number of milliseconds + ALLOCATIONS = 4; // Number of allocations + ID = 5; // Id + PERCENT = 6; // Percent + SECONDS = 115; // 's', Number of seconds (monotonic time) + } + optional DataUnit unit = 3; + } + repeated ValueDescriptor value_descriptors = 3; +}
\ No newline at end of file diff --git a/core/proto/android/util/log.proto b/core/proto/android/util/log.proto new file mode 100644 index 000000000000..30ff41242bcc --- /dev/null +++ b/core/proto/android/util/log.proto @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.util; + +option java_multiple_files = true; + +// Represents a Text Log in logd +// Next Tag: 9 +message TextLogEntry { + optional uint64 sec = 1; + optional uint64 nanosec = 2; + + enum LogPriority { + LOG_UNKNOWN = 0; + LOG_DEFAULT = 1; + LOG_VERBOSE = 2; + LOG_DEBUG = 3; + LOG_INFO = 4; + LOG_WARN = 5; + LOG_ERROR = 6; + LOG_FATAL = 7; + LOG_SILENT = 8; + } + optional LogPriority priority = 3; + optional int32 uid = 4; + optional int32 pid = 5; + optional int32 tid = 6; + optional string tag = 7; + optional string log = 8; +} + +// Represents a Binary Log in logd, need to look event-log-tags for more info. +// Next Tag: 8 +message BinaryLogEntry { + optional uint64 sec = 1; + optional uint64 nanosec = 2; + optional int32 uid = 3; + optional int32 pid = 4; + optional int32 tid = 5; + + // Index of the event tag, can look up in event-log-tags file + optional uint32 tag_index = 6; + + message Elem { + // must be sync with liblog log/log.h + enum Type { + EVENT_TYPE_LIST_STOP = 10; // '\n' + EVENT_TYPE_UNKNOWN = 63; // '?' + + EVENT_TYPE_INT = 0; + EVENT_TYPE_LONG = 1; + EVENT_TYPE_STRING = 2; + EVENT_TYPE_LIST = 3; + EVENT_TYPE_FLOAT = 4; + } + optional Type type = 1 [default=EVENT_TYPE_UNKNOWN]; + + oneof value { + int32 val_int32 = 2; + int64 val_int64 = 3; + string val_string = 4; + float val_float = 5; + } + } + repeated Elem elems = 7; +} + +message LogProto { + repeated TextLogEntry text_logs = 1; + + repeated BinaryLogEntry binary_logs = 2; +} + diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6b89489d586e..3a6b2de49311 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -159,6 +159,8 @@ <protected-broadcast android:name="android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED" /> <protected-broadcast + android:name="android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" /> @@ -173,6 +175,8 @@ <protected-broadcast android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast + android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index fd05bb442247..d2685cfb5a8f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2784,7 +2784,7 @@ <bool name="config_eap_sim_based_auth_supported">true</bool> <!-- How long history of previous vibrations should be kept for the dumpsys. --> - <integer name="config_previousVibrationsDumpLimit">20</integer> + <integer name="config_previousVibrationsDumpLimit">50</integer> <!-- The default vibration strength, must be between 1 and 255 inclusive. --> <integer name="config_defaultVibrationAmplitude">255</integer> diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java index 1a54bd608d48..63a5e4cc1dd8 100644 --- a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java +++ b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java @@ -41,6 +41,8 @@ public class PackageBackwardCompatibilityTest { private static final String ANDROID_TEST_MOCK = "android.test.mock"; + private static final String OTHER_LIBRARY = "other.library"; + private Package mPackage; private static ArrayList<String> arrayList(String... strings) { @@ -78,6 +80,18 @@ public class PackageBackwardCompatibilityTest { } @Test + public void targeted_at_O_not_empty_usesLibraries() { + mPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.O; + mPackage.usesLibraries = arrayList(OTHER_LIBRARY); + PackageBackwardCompatibility.modifySharedLibraries(mPackage); + // The org.apache.http.legacy jar should be added at the start of the list. + assertEquals("usesLibraries not updated correctly", + arrayList(ORG_APACHE_HTTP_LEGACY, OTHER_LIBRARY), + mPackage.usesLibraries); + assertNull("usesOptionalLibraries not updated correctly", mPackage.usesOptionalLibraries); + } + + @Test public void targeted_at_O_org_apache_http_legacy_in_usesLibraries() { mPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.O; mPackage.usesLibraries = arrayList(ORG_APACHE_HTTP_LEGACY); diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java index 90b457561180..566ac4daf950 100644 --- a/core/tests/coretests/src/android/os/WorkSourceTest.java +++ b/core/tests/coretests/src/android/os/WorkSourceTest.java @@ -331,4 +331,24 @@ public class WorkSourceTest extends TestCase { wc.addNode(200, "tag2"); assertEquals(100, wc.getAttributionUid()); } + + public void testRemove_fromChainedWorkSource() { + WorkSource ws1 = new WorkSource(); + ws1.createWorkChain().addNode(50, "foo"); + ws1.createWorkChain().addNode(75, "bar"); + ws1.add(100); + + WorkSource ws2 = new WorkSource(); + ws2.add(100); + + assertTrue(ws1.remove(ws2)); + assertEquals(2, ws1.getWorkChains().size()); + assertEquals(50, ws1.getWorkChains().get(0).getAttributionUid()); + assertEquals(75, ws1.getWorkChains().get(1).getAttributionUid()); + + ws2.createWorkChain().addNode(50, "foo"); + assertTrue(ws1.remove(ws2)); + assertEquals(1, ws1.getWorkChains().size()); + assertEquals(75, ws1.getWorkChains().get(0).getAttributionUid()); + } } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index fc8650086cd8..dfefbfd79551 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -112,6 +112,7 @@ public class SettingsBackupTest { Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD, Settings.Global.BATTERY_DISCHARGE_THRESHOLD, Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, + Settings.Global.BATTERY_STATS_CONSTANTS, Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX, @@ -452,7 +453,6 @@ public class SettingsBackupTest { Settings.Secure.COMPLETED_CATEGORY_PREFIX, Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, Settings.Secure.DEFAULT_INPUT_METHOD, - Settings.Secure.DEMO_USER_SETUP_COMPLETE, Settings.Secure.DEVICE_PAIRED, Settings.Secure.DIALER_DEFAULT_APPLICATION, Settings.Secure.DISABLED_PRINT_SERVICES, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java index 3794b5f61b5f..a8094ead2972 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java @@ -71,7 +71,7 @@ public class BatteryStatsImplTest { @Test public void testUpdateProcStateCpuTimes() { mBatteryStatsImpl.setOnBatteryInternal(true); - mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); + mBatteryStatsImpl.updateTimeBasesLocked(false, Display.STATE_ON, 0, 0); final int[] testUids = {10032, 10048, 10145, 10139}; final int[] testProcStates = { @@ -98,7 +98,7 @@ public class BatteryStatsImplTest { } } - mBatteryStatsImpl.updateProcStateCpuTimes(); + mBatteryStatsImpl.updateProcStateCpuTimes(true, false); verifyNoPendingUids(); for (int i = 0; i < testUids.length; ++i) { @@ -125,7 +125,7 @@ public class BatteryStatsImplTest { } addPendingUids(testUids, testProcStates); - mBatteryStatsImpl.updateProcStateCpuTimes(); + mBatteryStatsImpl.updateProcStateCpuTimes(true, false); verifyNoPendingUids(); for (int i = 0; i < testUids.length; ++i) { @@ -157,7 +157,7 @@ public class BatteryStatsImplTest { } addPendingUids(testUids, testProcStates); - mBatteryStatsImpl.updateProcStateCpuTimes(); + mBatteryStatsImpl.updateProcStateCpuTimes(true, true); verifyNoPendingUids(); for (int i = 0; i < testUids.length; ++i) { @@ -196,7 +196,7 @@ public class BatteryStatsImplTest { final long[] isolatedUidCpuTimes = {495784, 398473, 4895, 4905, 30984093}; when(mKernelSingleUidTimeReader.readDeltaMs(childUid)).thenReturn(isolatedUidCpuTimes); - mBatteryStatsImpl.updateProcStateCpuTimes(); + mBatteryStatsImpl.updateProcStateCpuTimes(true, true); verifyNoPendingUids(); for (int i = 0; i < testUids.length; ++i) { @@ -227,8 +227,8 @@ public class BatteryStatsImplTest { @Test public void testCopyFromAllUidsCpuTimes() { - mBatteryStatsImpl.setOnBatteryInternal(true); - mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0); + mBatteryStatsImpl.setOnBatteryInternal(false); + mBatteryStatsImpl.updateTimeBasesLocked(false, Display.STATE_ON, 0, 0); final int[] testUids = {10032, 10048, 10145, 10139}; final int[] testProcStates = { @@ -264,7 +264,7 @@ public class BatteryStatsImplTest { .thenReturn(expectedCpuTimes[i]); } - mBatteryStatsImpl.copyFromAllUidsCpuTimes(); + mBatteryStatsImpl.copyFromAllUidsCpuTimes(true, false); verifyNoPendingUids(); for (int i = 0; i < testUids.length; ++i) { diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java index e54fe7ddbf19..4d34721b5aba 100644 --- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java @@ -25,6 +25,8 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING; import static android.os.BatteryStats.Uid.UID_PROCESS_TYPES; +import static com.android.internal.os.BatteryStatsImpl.Constants.KEY_TRACK_CPU_TIMES_BY_PROC_STATE; + import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; @@ -48,15 +50,19 @@ import android.os.IBinder; import android.os.PowerManager; import android.os.Process; import android.os.SystemClock; +import android.provider.Settings; import android.support.test.InstrumentationRegistry; import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; +import android.text.TextUtils; import android.util.DebugUtils; import android.util.Log; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.junit.runner.RunWith; import java.util.Arrays; @@ -86,6 +92,9 @@ public class BstatsCpuTimesValidationTest { private static final int START_SERVICE_TIMEOUT_MS = 2000; private static final int START_ISOLATED_SERVICE_TIMEOUT_MS = 2000; + private static final int SETTING_UPDATE_TIMEOUT_MS = 2000; + private static final int SETTING_UPDATE_CHECK_INTERVAL_MS = 200; + private static final int GENERAL_TIMEOUT_MS = 4000; private static final int GENERAL_INTERVAL_MS = 200; @@ -97,6 +106,8 @@ public class BstatsCpuTimesValidationTest { private static boolean sCpuFreqTimesAvailable; private static boolean sPerProcStateTimesAvailable; + @Rule public TestName testName = new TestName(); + @BeforeClass public static void setupOnce() throws Exception { sContext = InstrumentationRegistry.getContext(); @@ -123,6 +134,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes() throws Exception { if (!sCpuFreqTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -148,6 +162,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_screenOff() throws Exception { if (!sCpuFreqTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -179,6 +196,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_isolatedProcess() throws Exception { if (!sCpuFreqTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -204,6 +224,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_stateTop() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -234,6 +257,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testIsolatedCpuFreqTimes_stateService() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -272,6 +298,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_stateTopSleeping() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -302,6 +331,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_stateFgService() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -332,6 +364,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_stateFg() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -362,6 +397,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_stateBg() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -392,6 +430,9 @@ public class BstatsCpuTimesValidationTest { @Test public void testCpuFreqTimes_stateCached() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); return; } @@ -419,6 +460,124 @@ public class BstatsCpuTimesValidationTest { batteryOffScreenOn(); } + @Test + public void testCpuFreqTimes_trackingDisabled() throws Exception { + if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { + Log.w(TAG, "Skipping " + testName.getMethodName() + + "; freqTimesAvailable=" + sCpuFreqTimesAvailable + + ", procStateTimesAvailable=" + sPerProcStateTimesAvailable); + return; + } + + final String bstatsConstants = Settings.Global.getString(sContext.getContentResolver(), + Settings.Global.BATTERY_STATS_CONSTANTS); + try { + batteryOnScreenOn(); + forceStop(); + resetBatteryStats(); + final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); + assertNull("Initial snapshot should be null, initial=" + + Arrays.toString(initialSnapshot), initialSnapshot); + assertNull("Initial top state snapshot should be null", + getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP)); + + doSomeWork(PROCESS_STATE_TOP); + forceStop(); + + final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); + final String msgCpuTimes = getAllCpuTimesMsg(); + assertCpuTimesValid(cpuTimesMs); + long actualCpuTimeMs = 0; + for (int i = 0; i < cpuTimesMs.length / 2; ++i) { + actualCpuTimeMs += cpuTimesMs[i]; + } + assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, + WORK_DURATION_MS, actualCpuTimeMs); + + updateTrackPerProcStateCpuTimesSetting(bstatsConstants, false); + + doSomeWork(PROCESS_STATE_TOP); + forceStop(); + + final long[] cpuTimesMs2 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); + assertCpuTimesValid(cpuTimesMs2); + assertCpuTimesEqual(cpuTimesMs2, cpuTimesMs, 20, + "Unexpected cpu times with tracking off"); + + updateTrackPerProcStateCpuTimesSetting(bstatsConstants, true); + + final long[] cpuTimesMs3 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); + assertCpuTimesValid(cpuTimesMs3); + assertCpuTimesEqual(cpuTimesMs3, cpuTimesMs, 20, + "Unexpected cpu times after turning on tracking"); + + doSomeWork(PROCESS_STATE_TOP); + forceStop(); + + final long[] cpuTimesMs4 = getAllCpuFreqTimes(sTestPkgUid, PROCESS_STATE_TOP); + assertCpuTimesValid(cpuTimesMs4); + actualCpuTimeMs = 0; + for (int i = 0; i < cpuTimesMs.length / 2; ++i) { + actualCpuTimeMs += cpuTimesMs[i]; + } + assertApproximateValue("Incorrect total cpu time, " + msgCpuTimes, + 2 * WORK_DURATION_MS, actualCpuTimeMs); + + batteryOffScreenOn(); + } finally { + Settings.Global.putString(sContext.getContentResolver(), + Settings.Global.BATTERY_STATS_CONSTANTS, bstatsConstants); + } + } + + private void assertCpuTimesEqual(long[] actual, long[] expected, long delta, String errMsg) { + for (int i = actual.length - 1; i >= 0; --i) { + if (actual[i] > expected[i] + delta || actual[i] < expected[i]) { + fail(errMsg + ", actual=" + actual + ", expected=" + expected + ", delta=" + delta); + } + } + } + + private void updateTrackPerProcStateCpuTimesSetting(String originalConstants, boolean enabled) + throws Exception { + final String newConstants; + final String setting = KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=" + enabled; + if (originalConstants == null || "null".equals(originalConstants)) { + newConstants = setting; + } else if (originalConstants.contains(KEY_TRACK_CPU_TIMES_BY_PROC_STATE)) { + newConstants = originalConstants.replaceAll( + KEY_TRACK_CPU_TIMES_BY_PROC_STATE + "=(true|false)", setting); + } else { + newConstants = originalConstants + "," + setting; + } + Settings.Global.putString(sContext.getContentResolver(), + Settings.Global.BATTERY_STATS_CONSTANTS, newConstants); + assertTrackPerProcStateCpuTimesSetting(enabled); + } + + private void assertTrackPerProcStateCpuTimesSetting(boolean enabled) throws Exception { + final String expectedValue = Boolean.toString(enabled); + assertDelayedCondition("Unexpected value for " + KEY_TRACK_CPU_TIMES_BY_PROC_STATE, () -> { + final String actualValue = getSettingValueFromDump(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); + return expectedValue.equals(actualValue) + ? null : "expected=" + expectedValue + ", actual=" + actualValue; + }, SETTING_UPDATE_TIMEOUT_MS, SETTING_UPDATE_CHECK_INTERVAL_MS); + } + + private String getSettingValueFromDump(String key) throws Exception { + final String settingsDump = executeCmdSilent("dumpsys batterystats --settings"); + final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n'); + splitter.setString(settingsDump); + String next; + while (splitter.hasNext()) { + next = splitter.next(); + if (next.startsWith(key)) { + return next.split("=")[1]; + } + } + return null; + } + private void assertCpuTimesValid(long[] cpuTimes) { assertNotNull(cpuTimes); for (int i = 0; i < cpuTimes.length; ++i) { @@ -750,13 +909,18 @@ public class BstatsCpuTimesValidationTest { } private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition) - throws Exception { - final long endTime = SystemClock.uptimeMillis() + GENERAL_TIMEOUT_MS; + throws Exception { + assertDelayedCondition(errMsgPrefix, condition, GENERAL_TIMEOUT_MS, GENERAL_INTERVAL_MS); + } + + private void assertDelayedCondition(String errMsgPrefix, ExpectedCondition condition, + long timeoutMs, long checkIntervalMs) throws Exception { + final long endTime = SystemClock.uptimeMillis() + timeoutMs; while (SystemClock.uptimeMillis() <= endTime) { if (condition.getErrIfNotTrue() == null) { return; } - SystemClock.sleep(GENERAL_INTERVAL_MS); + SystemClock.sleep(checkIntervalMs); } final String errMsg = condition.getErrIfNotTrue(); if (errMsg != null) { diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java index 27aec561dff2..37b4e41a38d6 100644 --- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java @@ -16,7 +16,9 @@ package com.android.internal.os; +import static android.os.BatteryStats.STATS_CURRENT; import static android.os.BatteryStats.STATS_SINCE_CHARGED; +import static android.os.BatteryStats.STATS_SINCE_UNPLUGGED; import static com.android.internal.os.BatteryStatsImpl.LongSamplingCounterArray; import static com.android.internal.os.BatteryStatsImpl.TimeBase; @@ -61,7 +63,6 @@ public class LongSamplingCounterArrayTest { private static final long[] COUNTS = {1111, 2222, 3333, 4444}; private static final long[] LOADED_COUNTS = {5555, 6666, 7777, 8888}; - private static final long[] PLUGGED_COUNTS = {9999, 11111, 22222, 33333}; private static final long[] UNPLUGGED_COUNTS = {44444, 55555, 66666, 77777}; private static final long[] ZEROES = {0, 0, 0, 0}; @@ -83,11 +84,10 @@ public class LongSamplingCounterArrayTest { parcel.setDataPosition(0); // Now clear counterArray and verify values are read from parcel correctly. - updateCounts(null, null, null, null); + updateCounts(null, null, null); mCounterArray = LongSamplingCounterArray.readFromParcel(parcel, mTimeBase); assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts"); assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); - assertArrayEquals(COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts"); assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); parcel.recycle(); @@ -101,11 +101,10 @@ public class LongSamplingCounterArrayTest { parcel.setDataPosition(0); // Now clear counterArray and verify values are read from parcel correctly. - updateCounts(null, null, null, null); + updateCounts(null, null, null); mCounterArray = LongSamplingCounterArray.readSummaryFromParcelLocked(parcel, mTimeBase); assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts"); assertArrayEquals(COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); - assertArrayEquals(COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts"); assertArrayEquals(COUNTS, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); parcel.recycle(); } @@ -116,8 +115,7 @@ public class LongSamplingCounterArrayTest { mCounterArray.onTimeStarted(0, 0, 0); assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts"); assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); - assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts"); - assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mUnpluggedCounts, + assertArrayEquals(COUNTS, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); } @@ -127,7 +125,6 @@ public class LongSamplingCounterArrayTest { mCounterArray.onTimeStopped(0, 0, 0); assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts"); assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); - assertArrayEquals(COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts"); assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); } @@ -137,24 +134,50 @@ public class LongSamplingCounterArrayTest { initializeCounterArrayWithDefaultValues(); when(mTimeBase.isRunning()).thenReturn(false); - long[] actualVal = mCounterArray.getCountsLocked(STATS_SINCE_CHARGED); - long[] expectedVal = PLUGGED_COUNTS; - assertArrayEquals(expectedVal, actualVal, "Unexpected values"); + assertArrayEquals(COUNTS, mCounterArray.getCountsLocked(STATS_SINCE_CHARGED), + "Unexpected values"); + assertArrayEquals(subtract(COUNTS, LOADED_COUNTS), + mCounterArray.getCountsLocked(STATS_CURRENT), "Unexpected values"); + assertArrayEquals(subtract(COUNTS, UNPLUGGED_COUNTS), + mCounterArray.getCountsLocked(STATS_SINCE_UNPLUGGED), "Unexpected values"); when(mTimeBase.isRunning()).thenReturn(true); - actualVal = mCounterArray.getCountsLocked(STATS_SINCE_CHARGED); - expectedVal = COUNTS; - assertArrayEquals(expectedVal, actualVal, "Unexpected values"); + assertArrayEquals(COUNTS, mCounterArray.getCountsLocked(STATS_SINCE_CHARGED), + "Unexpected values"); + assertArrayEquals(subtract(COUNTS, LOADED_COUNTS), + mCounterArray.getCountsLocked(STATS_CURRENT), "Unexpected values"); + assertArrayEquals(subtract(COUNTS, UNPLUGGED_COUNTS), + mCounterArray.getCountsLocked(STATS_SINCE_UNPLUGGED), "Unexpected values"); + } + + private long[] subtract(long[] val, long[] toSubtract) { + final long[] result = val.clone(); + if (toSubtract != null) { + for (int i = val.length - 1; i >= 0; --i) { + result[i] -= toSubtract[i]; + } + } + return result; } @Test public void testAddCountLocked() { + updateCounts(null, null, null); final long[] deltas = {123, 234, 345, 456}; when(mTimeBase.isRunning()).thenReturn(true); mCounterArray.addCountLocked(deltas); assertArrayEquals(deltas, mCounterArray.mCounts, "Unexpected counts"); assertArrayEquals(null, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); - assertArrayEquals(null, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts"); + assertArrayEquals(null, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); + + updateCounts(null, null, null); + mCounterArray.addCountLocked(deltas, false); + assertArrayEquals(null, mCounterArray.mCounts, "Unexpected counts"); + assertArrayEquals(null, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); + assertArrayEquals(null, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); + mCounterArray.addCountLocked(deltas, true); + assertArrayEquals(deltas, mCounterArray.mCounts, "Unexpected counts"); + assertArrayEquals(null, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); assertArrayEquals(null, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); initializeCounterArrayWithDefaultValues(); @@ -165,7 +188,18 @@ public class LongSamplingCounterArrayTest { mCounterArray.addCountLocked(deltas); assertArrayEquals(newCounts, mCounterArray.mCounts, "Unexpected counts"); assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); - assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts"); + assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts, + "Unexpected unpluggedCounts"); + + initializeCounterArrayWithDefaultValues(); + mCounterArray.addCountLocked(deltas, false); + assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts"); + assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); + assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts, + "Unexpected unpluggedCounts"); + mCounterArray.addCountLocked(deltas, true); + assertArrayEquals(newCounts, mCounterArray.mCounts, "Unexpected counts"); + assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); assertArrayEquals(UNPLUGGED_COUNTS, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); } @@ -177,7 +211,6 @@ public class LongSamplingCounterArrayTest { mCounterArray.reset(false /* detachIfReset */); assertArrayEquals(ZEROES, mCounterArray.mCounts, "Unexpected counts"); assertArrayEquals(ZEROES, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); - assertArrayEquals(ZEROES, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts"); assertArrayEquals(ZEROES, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); verifyZeroInteractions(mTimeBase); @@ -186,7 +219,6 @@ public class LongSamplingCounterArrayTest { mCounterArray.reset(true /* detachIfReset */); assertArrayEquals(ZEROES, mCounterArray.mCounts, "Unexpected counts"); assertArrayEquals(ZEROES, mCounterArray.mLoadedCounts, "Unexpected loadedCounts"); - assertArrayEquals(ZEROES, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts"); assertArrayEquals(ZEROES, mCounterArray.mUnpluggedCounts, "Unexpected unpluggedCounts"); verify(mTimeBase).remove(mCounterArray); verifyNoMoreInteractions(mTimeBase); @@ -200,7 +232,7 @@ public class LongSamplingCounterArrayTest { } private void initializeCounterArrayWithDefaultValues() { - updateCounts(COUNTS, LOADED_COUNTS, PLUGGED_COUNTS, UNPLUGGED_COUNTS); + updateCounts(COUNTS, LOADED_COUNTS, UNPLUGGED_COUNTS); } private void assertArrayEquals(long[] expected, long[] actual, String msg) { @@ -208,11 +240,9 @@ public class LongSamplingCounterArrayTest { + ", actual: " + Arrays.toString(actual), Arrays.equals(expected, actual)); } - private void updateCounts(long[] counts, long[] loadedCounts, - long[] pluggedCounts, long[] unpluggedCounts) { - mCounterArray.mCounts = counts; - mCounterArray.mLoadedCounts = loadedCounts; - mCounterArray.mPluggedCounts = pluggedCounts; - mCounterArray.mUnpluggedCounts = unpluggedCounts; + private void updateCounts(long[] counts, long[] loadedCounts, long[] unpluggedCounts) { + mCounterArray.mCounts = counts == null ? null : counts.clone(); + mCounterArray.mLoadedCounts = loadedCounts == null ? null : loadedCounts.clone(); + mCounterArray.mUnpluggedCounts = unpluggedCounts == null ? null : unpluggedCounts.clone(); } } diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index f19ff6708c69..6c5a2aac159b 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -139,12 +139,19 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } @Override - public Future<?> scheduleReadProcStateCpuTimes() { + public Future<?> scheduleCpuSyncDueToSettingChange() { return null; } @Override - public Future<?> scheduleCopyFromAllUidsCpuTimes() { + public Future<?> scheduleReadProcStateCpuTimes( + boolean onBattery, boolean onBatteryScreenOff) { + return null; + } + + @Override + public Future<?> scheduleCopyFromAllUidsCpuTimes( + boolean onBattery, boolean onBatteryScreenOff) { return null; } diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 60416a720231..97ce88606331 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -16,29 +16,44 @@ package android.graphics; +import static android.system.OsConstants.SEEK_SET; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RawRes; +import android.content.ContentResolver; +import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.NinePatchDrawable; +import android.net.Uri; +import android.system.ErrnoException; +import android.system.Os; + +import libcore.io.IoUtils; +import dalvik.system.CloseGuard; import java.nio.ByteBuffer; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.ArrayIndexOutOfBoundsException; +import java.lang.AutoCloseable; import java.lang.NullPointerException; import java.lang.RuntimeException; import java.lang.annotation.Retention; import static java.lang.annotation.RetentionPolicy.SOURCE; +import java.util.concurrent.atomic.AtomicBoolean; /** * Class for decoding images as {@link Bitmap}s or {@link Drawable}s. * @hide */ -public final class ImageDecoder { +public final class ImageDecoder implements AutoCloseable { /** * Source of the encoded image data. */ @@ -47,10 +62,7 @@ public final class ImageDecoder { Resources getResources() { return null; } /* @hide */ - void close() {} - - /* @hide */ - abstract ImageDecoder createImageDecoder(); + abstract ImageDecoder createImageDecoder() throws IOException; }; private static class ByteArraySource extends Source { @@ -64,7 +76,7 @@ public final class ImageDecoder { private final int mLength; @Override - public ImageDecoder createImageDecoder() { + public ImageDecoder createImageDecoder() throws IOException { return nCreate(mData, mOffset, mLength); } } @@ -76,7 +88,7 @@ public final class ImageDecoder { private final ByteBuffer mBuffer; @Override - public ImageDecoder createImageDecoder() { + public ImageDecoder createImageDecoder() throws IOException { if (!mBuffer.isDirect() && mBuffer.hasArray()) { int offset = mBuffer.arrayOffset() + mBuffer.position(); int length = mBuffer.limit() - mBuffer.position(); @@ -86,61 +98,110 @@ public final class ImageDecoder { } } - private static class ResourceSource extends Source { - ResourceSource(Resources res, int resId) - throws Resources.NotFoundException { - // Test that the resource can be found. - InputStream is = null; + private static class ContentResolverSource extends Source { + ContentResolverSource(ContentResolver resolver, Uri uri) { + mResolver = resolver; + mUri = uri; + } + + private final ContentResolver mResolver; + private final Uri mUri; + + @Override + public ImageDecoder createImageDecoder() throws IOException { + AssetFileDescriptor assetFd = null; try { - is = res.openRawResource(resId); + if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) { + assetFd = mResolver.openTypedAssetFileDescriptor(mUri, + "image/*", null); + } else { + assetFd = mResolver.openAssetFileDescriptor(mUri, "r"); + } + } catch (FileNotFoundException e) { + // Some images cannot be opened as AssetFileDescriptors (e.g. + // bmp, ico). Open them as InputStreams. + InputStream is = mResolver.openInputStream(mUri); + if (is == null) { + throw new FileNotFoundException(mUri.toString()); + } + + return createFromStream(is); + } + + final FileDescriptor fd = assetFd.getFileDescriptor(); + final long offset = assetFd.getStartOffset(); + + ImageDecoder decoder = null; + try { + try { + Os.lseek(fd, offset, SEEK_SET); + decoder = nCreate(fd); + } catch (ErrnoException e) { + decoder = createFromStream(new FileInputStream(fd)); + } } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } + if (decoder == null) { + IoUtils.closeQuietly(assetFd); + } else { + decoder.mAssetFd = assetFd; } } + return decoder; + } + } + + private static ImageDecoder createFromStream(InputStream is) throws IOException { + // Arbitrary size matches BitmapFactory. + byte[] storage = new byte[16 * 1024]; + ImageDecoder decoder = null; + try { + decoder = nCreate(is, storage); + } finally { + if (decoder == null) { + IoUtils.closeQuietly(is); + } else { + decoder.mInputStream = is; + decoder.mTempStorage = storage; + } + } + + return decoder; + } + private static class ResourceSource extends Source { + ResourceSource(Resources res, int resId) { mResources = res; mResId = resId; } final Resources mResources; final int mResId; - // This is just stored here in order to keep the underlying Asset - // alive. FIXME: Can I access the Asset (and keep it alive) without - // this object? - InputStream mInputStream; @Override public Resources getResources() { return mResources; } @Override - public ImageDecoder createImageDecoder() { - // FIXME: Can I bypass creating the stream? - try { - mInputStream = mResources.openRawResource(mResId); - } catch (Resources.NotFoundException e) { - // This should never happen, since we already tested in the - // constructor. - } - if (!(mInputStream instanceof AssetManager.AssetInputStream)) { - // This should never happen. - throw new RuntimeException("Resource is not an asset?"); - } - long asset = ((AssetManager.AssetInputStream) mInputStream).getNativeAsset(); - return nCreate(asset); - } - - @Override - public void close() { + public ImageDecoder createImageDecoder() throws IOException { + // This is just used in order to access the underlying Asset and + // keep it alive. FIXME: Can we skip creating this object? + InputStream is = null; + ImageDecoder decoder = null; try { - mInputStream.close(); - } catch (IOException e) { + is = mResources.openRawResource(mResId); + if (!(is instanceof AssetManager.AssetInputStream)) { + // This should never happen. + throw new RuntimeException("Resource is not an asset?"); + } + long asset = ((AssetManager.AssetInputStream) is).getNativeAsset(); + decoder = nCreate(asset); } finally { - mInputStream = null; + if (decoder == null) { + IoUtils.closeQuietly(is); + } else { + decoder.mInputStream = is; + } } + return decoder; } } @@ -159,18 +220,23 @@ public final class ImageDecoder { }; /** - * Used if the provided data is incomplete. + * Supplied to onException if the provided data is incomplete. + * + * Will never be thrown by ImageDecoder. * * There may be a partial image to display. */ - public class IncompleteException extends Exception {}; + public static class IncompleteException extends IOException {}; /** * Used if the provided data is corrupt. * - * There may be a partial image to display. + * May be thrown if there is nothing to display. + * + * If supplied to onException, there may be a correct partial image to + * display. */ - public class CorruptException extends Exception {}; + public static class CorruptException extends IOException {}; /** * Optional listener supplied to {@link #decodeDrawable} or @@ -193,15 +259,14 @@ public final class ImageDecoder { public static interface OnExceptionListener { /** * Called when there is a problem in the stream or in the data. - * FIXME: Or do not allow streams? * FIXME: Report how much of the image has been decoded? * - * @param e Exception containing information about the error. + * @param e IOException containing information about the error. * @return True to create and return a {@link Drawable}/ * {@link Bitmap} with partial data. False to return * {@code null}. True is the default. */ - public boolean onException(Exception e); + public boolean onException(IOException e); }; // Fields @@ -221,9 +286,15 @@ public final class ImageDecoder { private PostProcess mPostProcess; private OnExceptionListener mOnExceptionListener; + // Objects for interacting with the input. + private InputStream mInputStream; + private byte[] mTempStorage; + private AssetFileDescriptor mAssetFd; + private final AtomicBoolean mClosed = new AtomicBoolean(); + private final CloseGuard mCloseGuard = CloseGuard.get(); /** - * Private constructor called by JNI. {@link #recycle} must be + * Private constructor called by JNI. {@link #close} must be * called after decoding to delete native resources. */ @SuppressWarnings("unused") @@ -233,6 +304,20 @@ public final class ImageDecoder { mHeight = height; mDesiredWidth = width; mDesiredHeight = height; + mCloseGuard.open("close"); + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + + close(); + } finally { + super.finalize(); + } } /** @@ -243,14 +328,28 @@ public final class ImageDecoder { * // FIXME: Can be an @DrawableRes? * @return a new Source object, which can be passed to * {@link #decodeDrawable} or {@link #decodeBitmap}. - * @throws Resources.NotFoundException if the asset does not exist. */ + @NonNull public static Source createSource(@NonNull Resources res, @RawRes int resId) - throws Resources.NotFoundException { + { return new ResourceSource(res, resId); } /** + * Create a new {@link Source} from a {@link android.net.Uri}. + * + * @param cr to retrieve from. + * @param uri of the image file. + * @return a new Source object, which can be passed to + * {@link #decodeDrawable} or {@link #decodeBitmap}. + */ + @NonNull + public static Source createSource(@NonNull ContentResolver cr, + @NonNull Uri uri) { + return new ContentResolverSource(cr, uri); + } + + /** * Create a new {@link Source} from a byte array. * @param data byte array of compressed image data. * @param offset offset into data for where the decoder should begin @@ -307,7 +406,7 @@ public final class ImageDecoder { + "provided " + sampleSize); } if (mNativePtr == 0) { - throw new IllegalStateException("ImageDecoder is recycled!"); + throw new IllegalStateException("ImageDecoder is closed!"); } return nGetSampledSize(mNativePtr, sampleSize); @@ -500,24 +599,26 @@ public final class ImageDecoder { mAsAlphaMask = true; } - /** - * Clean up resources. - * - * ImageDecoder has a private constructor, and will always be recycled - * by decodeDrawable or decodeBitmap which creates it, so there is no - * need for a finalizer. - */ - private void recycle() { - if (mNativePtr == 0) { + @Override + public void close() { + mCloseGuard.close(); + if (!mClosed.compareAndSet(false, true)) { return; } - nRecycle(mNativePtr); + nClose(mNativePtr); mNativePtr = 0; + + IoUtils.closeQuietly(mInputStream); + IoUtils.closeQuietly(mAssetFd); + + mInputStream = null; + mAssetFd = null; + mTempStorage = null; } private void checkState() { if (mNativePtr == 0) { - throw new IllegalStateException("Cannot reuse ImageDecoder.Source!"); + throw new IllegalStateException("Cannot use closed ImageDecoder!"); } checkSubset(mDesiredWidth, mDesiredHeight, mCropRect); @@ -548,44 +649,47 @@ public final class ImageDecoder { /** * Create a {@link Drawable}. + * @throws IOException if {@code src} is not found, is an unsupported + * format, or cannot be decoded for any reason. */ - public static Drawable decodeDrawable(Source src, OnHeaderDecodedListener listener) { - ImageDecoder decoder = src.createImageDecoder(); - if (decoder == null) { - return null; - } - - if (listener != null) { - ImageInfo info = new ImageInfo(decoder.mWidth, decoder.mHeight); - listener.onHeaderDecoded(info, decoder); - } + @NonNull + public static Drawable decodeDrawable(Source src, OnHeaderDecodedListener listener) + throws IOException { + try (ImageDecoder decoder = src.createImageDecoder()) { + if (listener != null) { + ImageInfo info = new ImageInfo(decoder.mWidth, decoder.mHeight); + listener.onHeaderDecoded(info, decoder); + } - decoder.checkState(); + decoder.checkState(); - if (decoder.mRequireUnpremultiplied) { - // Though this could be supported (ignored) for opaque images, it - // seems better to always report this error. - throw new IllegalStateException("Cannot decode a Drawable with" + - " unpremultiplied pixels!"); - } + if (decoder.mRequireUnpremultiplied) { + // Though this could be supported (ignored) for opaque images, + // it seems better to always report this error. + throw new IllegalStateException("Cannot decode a Drawable " + + "with unpremultiplied pixels!"); + } - if (decoder.mMutable) { - throw new IllegalStateException("Cannot decode a mutable Drawable!"); - } + if (decoder.mMutable) { + throw new IllegalStateException("Cannot decode a mutable " + + "Drawable!"); + } - try { Bitmap bm = nDecodeBitmap(decoder.mNativePtr, decoder.mOnExceptionListener, decoder.mPostProcess, - decoder.mDesiredWidth, decoder.mDesiredHeight, + decoder.mDesiredWidth, + decoder.mDesiredHeight, decoder.mCropRect, - false, // decoder.mMutable + false, // mMutable decoder.mAllocator, - false, // decoder.mRequireUnpremultiplied + false, // mRequireUnpremultiplied decoder.mPreferRamOverQuality, - decoder.mAsAlphaMask - ); + decoder.mAsAlphaMask); if (bm == null) { + // FIXME: bm should never be null. Currently a return value + // of false from onException will result in bm being null. What + // is the right API to choose to discard partial Bitmaps? return null; } @@ -606,60 +710,58 @@ public final class ImageDecoder { // TODO: Handle animation. return new BitmapDrawable(res, bm); - } finally { - decoder.recycle(); - src.close(); } } /** - * Create a {@link Bitmap}. + * Create a {@link Bitmap}. + * @throws IOException if {@code src} is not found, is an unsupported + * format, or cannot be decoded for any reason. */ - public static Bitmap decodeBitmap(Source src, OnHeaderDecodedListener listener) { - ImageDecoder decoder = src.createImageDecoder(); - if (decoder == null) { - return null; - } - - if (listener != null) { - ImageInfo info = new ImageInfo(decoder.mWidth, decoder.mHeight); - listener.onHeaderDecoded(info, decoder); - } + @NonNull + public static Bitmap decodeBitmap(Source src, OnHeaderDecodedListener listener) + throws IOException { + try (ImageDecoder decoder = src.createImageDecoder()) { + if (listener != null) { + ImageInfo info = new ImageInfo(decoder.mWidth, decoder.mHeight); + listener.onHeaderDecoded(info, decoder); + } - decoder.checkState(); + decoder.checkState(); - try { return nDecodeBitmap(decoder.mNativePtr, decoder.mOnExceptionListener, decoder.mPostProcess, - decoder.mDesiredWidth, decoder.mDesiredHeight, + decoder.mDesiredWidth, + decoder.mDesiredHeight, decoder.mCropRect, decoder.mMutable, decoder.mAllocator, decoder.mRequireUnpremultiplied, decoder.mPreferRamOverQuality, decoder.mAsAlphaMask); - } finally { - decoder.recycle(); - src.close(); } } - private static native ImageDecoder nCreate(long asset); + private static native ImageDecoder nCreate(long asset) throws IOException; private static native ImageDecoder nCreate(ByteBuffer buffer, int position, - int limit); + int limit) throws IOException; private static native ImageDecoder nCreate(byte[] data, int offset, - int length); + int length) throws IOException; + private static native ImageDecoder nCreate(InputStream is, byte[] storage); + private static native ImageDecoder nCreate(FileDescriptor fd) throws IOException; + @NonNull private static native Bitmap nDecodeBitmap(long nativePtr, OnExceptionListener listener, PostProcess postProcess, int width, int height, Rect cropRect, boolean mutable, int allocator, boolean requireUnpremul, - boolean preferRamOverQuality, boolean asAlphaMask); + boolean preferRamOverQuality, boolean asAlphaMask) + throws IOException; private static native Point nGetSampledSize(long nativePtr, int sampleSize); private static native void nGetPadding(long nativePtr, Rect outRect); - private static native void nRecycle(long nativePtr); + private static native void nClose(long nativePtr); } diff --git a/libs/incident/proto/android/section.proto b/libs/incident/proto/android/section.proto index d268cf4fc09a..49bfe1e8a598 100644 --- a/libs/incident/proto/android/section.proto +++ b/libs/incident/proto/android/section.proto @@ -37,6 +37,9 @@ enum SectionType { // incidentd calls dumpsys for annotated field SECTION_DUMPSYS = 3; + + // incidentd calls logs for annotated field + SECTION_LOG = 4; } message SectionFlags { diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index f9075cfd10d9..0990dcccaecb 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -79,6 +79,7 @@ interface ILocationManager boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName); void flushGnssBatch(String packageName); boolean stopGnssBatch(); + boolean injectLocation(in Location location); // --- deprecated --- List<String> getAllProviders(); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 4802b2357906..f0b2774a4276 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -42,6 +42,7 @@ import java.util.List; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.LOCATION_HARDWARE; /** * This class provides access to the system location services. These @@ -882,6 +883,34 @@ public class LocationManager { requestLocationUpdates(request, null, null, intent); } + /** + * Set the last known location with a new location. + * + * <p>A privileged client can inject a {@link Location} if it has a better estimate of what + * the recent location is. This is especially useful when the device boots up and the GPS + * chipset is in the process of getting the first fix. If the client has cached the location, + * it can inject the {@link Location}, so if an app requests for a {@link Location} from {@link + * #getLastKnownLocation(String)}, the location information is still useful before getting + * the first fix.</p> + * + * <p> Useful in products like Auto. + * + * @param newLocation newly available {@link Location} object + * @return true if update was successful, false if not + * + * @throws SecurityException if no suitable permission is present + * + * @hide + */ + @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION}) + public boolean injectLocation(Location newLocation) { + try { + return mService.injectLocation(newLocation); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private ListenerTransport wrapListener(LocationListener listener, Looper looper) { if (listener == null) return null; synchronized (mListeners) { diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk index 19e44e36447d..6feb8a6294d7 100644 --- a/packages/PrintSpooler/Android.mk +++ b/packages/PrintSpooler/Android.mk @@ -18,17 +18,26 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res frameworks/support/v7/recyclerview/res -LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages android.support.v7.recyclerview +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res +LOCAL_USE_AAPT2 := true LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES += \ - src/com/android/printspooler/renderer/IPdfRenderer.aidl \ - src/com/android/printspooler/renderer/IPdfEditor.aidl + src/com/android/printspooler/renderer/IPdfRenderer.aidl \ + src/com/android/printspooler/renderer/IPdfEditor.aidl LOCAL_PACKAGE_NAME := PrintSpooler LOCAL_JNI_SHARED_LIBRARIES := libprintspooler_jni -LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview +LOCAL_STATIC_ANDROID_LIBRARIES := \ + android-support-v7-recyclerview \ + android-support-compat \ + android-support-media-compat \ + android-support-core-utils \ + android-support-core-ui \ + android-support-fragment + +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-annotations include $(BUILD_PACKAGE) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 1d3f26eec56d..48de1c92e890 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1720,9 +1720,6 @@ class SettingsProtoDumpUtil { Settings.Secure.QS_TILES, SecureSettingsProto.QS_TILES); dumpSetting(s, p, - Settings.Secure.DEMO_USER_SETUP_COMPLETE, - SecureSettingsProto.DEMO_USER_SETUP_COMPLETE); - dumpSetting(s, p, Settings.Secure.INSTANT_APPS_ENABLED, SecureSettingsProto.INSTANT_APPS_ENABLED); dumpSetting(s, p, diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml index b154d46f162c..5e09e754f9a5 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml @@ -32,6 +32,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/widget_vertical_padding" + android:paddingStart="64dp" + android:paddingEnd="64dp" android:theme="@style/TextAppearance.Keyguard" /> <LinearLayout android:id="@+id/row" diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 463af61c16f1..04cf6b0e29ea 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -57,7 +57,7 @@ <dimen name="widget_separator_thickness">2dp</dimen> <dimen name="widget_horizontal_padding">8dp</dimen> <dimen name="widget_icon_size">16dp</dimen> - <dimen name="widget_icon_padding">4dp</dimen> + <dimen name="widget_icon_padding">8dp</dimen> <!-- The y translation to apply at the start in appear animations. --> <dimen name="appear_y_translation_start">32dp</dimen> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index d50bab533856..5f52e2ac10a9 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -83,13 +83,13 @@ <item name="android:gravity">center</item> <item name="android:ellipsize">end</item> <item name="android:maxLines">2</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item> </style> <style name="TextAppearance.Keyguard.Secondary"> <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:textSize">@dimen/widget_label_font_size</item> - <item name="android:singleLine">true</item> </style> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index b8adb6a91bf2..8135c616d87e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -26,6 +26,9 @@ import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.net.Uri; import android.provider.Settings; +import android.text.Layout; +import android.text.TextUtils; +import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -40,6 +43,7 @@ import com.android.systemui.R; import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.tuner.TunerService; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.function.Consumer; @@ -129,7 +133,20 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe android.app.slice.SliceItem.FORMAT_TEXT, new String[]{android.app.slice.Slice.HINT_TITLE}, null /* nonHints */); - mTitle.setText(mainTitle.getText()); + CharSequence title = mainTitle.getText(); + mTitle.setText(title); + + // Check if we're already ellipsizing the text. + // We're going to figure out the best possible line break if not. + Layout layout = mTitle.getLayout(); + if (layout != null){ + final int lineCount = layout.getLineCount(); + if (lineCount > 0) { + if (layout.getEllipsisCount(lineCount - 1) == 0) { + mTitle.setText(findBestLineBreak(title)); + } + } + } } mClickActions.clear(); @@ -195,6 +212,46 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mListener.accept(mHasHeader); } + /** + * Breaks a string in 2 lines where both have similar character count + * but first line is always longer. + * + * @param charSequence Original text. + * @return Optimal string. + */ + private CharSequence findBestLineBreak(CharSequence charSequence) { + if (TextUtils.isEmpty(charSequence)) { + return charSequence; + } + + String source = charSequence.toString(); + // Ignore if there is only 1 word, + // or if line breaks were manually set. + if (source.contains("\n") || !source.contains(" ")) { + return source; + } + + final String[] words = source.split(" "); + final StringBuilder optimalString = new StringBuilder(source.length()); + int current = 0; + while (optimalString.length() < source.length() - optimalString.length()) { + optimalString.append(words[current]); + if (current < words.length - 1) { + optimalString.append(" "); + } + current++; + } + optimalString.append("\n"); + for (int i = current; i < words.length; i++) { + optimalString.append(words[i]); + if (current < words.length - 1) { + optimalString.append(" "); + } + } + + return optimalString.toString(); + } + public void setDark(float darkAmount) { mDarkAmount = darkAmount; updateTextColors(); @@ -287,6 +344,9 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe setPadding(horizontalPadding, 0, horizontalPadding, 0); setCompoundDrawablePadding((int) context.getResources() .getDimension(R.dimen.widget_icon_padding)); + setMaxWidth(KeyguardSliceView.this.getWidth() / 2); + setMaxLines(1); + setEllipsize(TruncateAt.END); } public void setHasDivider(boolean hasDivider) { diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 22922e7bbac4..a7d1f0d977c6 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -146,6 +146,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis filter.addDataScheme("package"); filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(), PatternMatcher.PATTERN_LITERAL); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); mContext.registerReceiver(mLauncherAddedReceiver, filter); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index b352ec97b209..75f1b501b3f4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -16,6 +16,8 @@ package com.android.systemui.doze; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; + import android.app.AlarmManager; import android.content.Context; import android.os.Handler; @@ -79,6 +81,11 @@ public class DozeUi implements DozeMachine.Part { public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case DOZE_AOD: + if (oldState == DOZE_AOD_PAUSED) { + mHost.dozeTimeTick(); + } + scheduleTimeTick(); + break; case DOZE_AOD_PAUSING: scheduleTimeTick(); break; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index c35f5917d6cc..8bdbf28b5cf1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -101,6 +101,9 @@ public class LocationTile extends QSTileImpl<BooleanState> { // state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing()); state.value = locationEnabled; checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_SHARE_LOCATION); + if (state.disabledByPolicy == false) { + checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION_MODE); + } state.icon = mIcon; state.slash.isSlashed = !state.value; if (locationEnabled) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 2796f0ffa697..392581de8607 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -199,8 +199,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } - private final OverviewProxyListener mOverviewProxyListener = - isConnected -> setSlippery(!isConnected); + private final OverviewProxyListener mOverviewProxyListener = isConnected -> { + setSlippery(!isConnected); + setDisabledFlags(mDisabledFlags, true); + }; public NavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java index e117969c5993..0a3e34ee951d 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java @@ -68,7 +68,6 @@ public class UsbConfirmActivity extends AlertActivity String appName = mResolveInfo.loadLabel(packageManager).toString(); final AlertController.AlertParams ap = mAlertParams; - ap.mIcon = mResolveInfo.loadIcon(packageManager); ap.mTitle = appName; if (mDevice == null) { ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName, diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java index 4606aee346cc..238407a9a6f1 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -90,7 +90,6 @@ public class UsbPermissionActivity extends AlertActivity String appName = aInfo.loadLabel(packageManager).toString(); final AlertController.AlertParams ap = mAlertParams; - ap.mIcon = aInfo.loadIcon(packageManager); ap.mTitle = appName; if (mDevice == null) { ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName, diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java index 3e6bd7e5bfe4..2398fd3c4712 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java @@ -61,7 +61,7 @@ class DozeHostFake implements DozeHost { @Override public void dozeTimeTick() { - throw new RuntimeException("not implemented"); + // Nothing to do in here. Real host would just update the UI. } @Override diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 472723dfa909..a024d5a8e3ff 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -149,6 +149,9 @@ class AlarmManagerService extends SystemService { private long mNextNonWakeup; private long mLastWakeupSet; private long mLastWakeup; + private long mLastTickSet; + private long mLastTickIssued; // elapsed + private long mLastTickReceived; int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; @@ -1596,6 +1599,7 @@ class AlarmManagerService extends SystemService { pw.println(); mForceAppStandbyTracker.dump(pw, " "); + pw.println(); final long nowRTC = System.currentTimeMillis(); final long nowELAPSED = SystemClock.elapsedRealtime(); @@ -1607,8 +1611,12 @@ class AlarmManagerService extends SystemService { pw.println(); pw.print(" mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime); pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime))); - pw.print(" mLastTimeChangeRealtime="); - TimeUtils.formatDuration(mLastTimeChangeRealtime, pw); + pw.print(" mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime); + pw.print(" mLastTickIssued="); + TimeUtils.formatDuration(mLastTickIssued - nowELAPSED, pw); + pw.println(); + pw.print(" mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived))); + pw.print(" mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet))); pw.println(); if (!mInteractive) { pw.print(" Time since non-interactive: "); @@ -3284,6 +3292,9 @@ class AlarmManagerService extends SystemService { if (DEBUG_BATCH) { Slog.v(TAG, "Received TIME_TICK alarm; rescheduling"); } + synchronized (mLock) { + mLastTickReceived = System.currentTimeMillis(); + } scheduleTimeTickEvent(); } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) { // Since the kernel does not keep track of DST, we need to @@ -3309,6 +3320,11 @@ class AlarmManagerService extends SystemService { setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, 0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid(), "android"); + + // Finally, remember when we set the tick alarm + synchronized (mLock) { + mLastTickSet = currentTime; + } } public void scheduleDateChangedEvent() { @@ -3660,6 +3676,11 @@ class AlarmManagerService extends SystemService { if (alarm.operation != null) { // PendingIntent alarm mSendCount++; + + if (alarm.priorityClass.priority == PRIO_TICK) { + mLastTickIssued = nowELAPSED; + } + try { alarm.operation.send(getContext(), 0, mBackgroundIntent.putExtra( diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 6f697c46098b..985f16d910bc 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -795,64 +795,65 @@ public class DeviceIdleController extends SystemService Slog.e(TAG, "Bad device idle settings", e); } - LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong( + LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getDurationMillis( KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT, !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L); - LIGHT_PRE_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_PRE_IDLE_TIMEOUT, + LIGHT_PRE_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_PRE_IDLE_TIMEOUT, !COMPRESS_TIME ? 10 * 60 * 1000L : 30 * 1000L); - LIGHT_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_TIMEOUT, + LIGHT_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_IDLE_TIMEOUT, !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L); LIGHT_IDLE_FACTOR = mParser.getFloat(KEY_LIGHT_IDLE_FACTOR, 2f); - LIGHT_MAX_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_MAX_IDLE_TIMEOUT, + LIGHT_MAX_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_LIGHT_MAX_IDLE_TIMEOUT, !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L); - LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mParser.getLong( + LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mParser.getDurationMillis( KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET, !COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L); - LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mParser.getLong( + LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mParser.getDurationMillis( KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET, !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L); - MIN_LIGHT_MAINTENANCE_TIME = mParser.getLong( + MIN_LIGHT_MAINTENANCE_TIME = mParser.getDurationMillis( KEY_MIN_LIGHT_MAINTENANCE_TIME, !COMPRESS_TIME ? 5 * 1000L : 1 * 1000L); - MIN_DEEP_MAINTENANCE_TIME = mParser.getLong( + MIN_DEEP_MAINTENANCE_TIME = mParser.getDurationMillis( KEY_MIN_DEEP_MAINTENANCE_TIME, !COMPRESS_TIME ? 30 * 1000L : 5 * 1000L); long inactiveTimeoutDefault = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L; - INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT, + INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_INACTIVE_TIMEOUT, !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10)); - SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT, + SENSING_TIMEOUT = mParser.getDurationMillis(KEY_SENSING_TIMEOUT, !DEBUG ? 4 * 60 * 1000L : 60 * 1000L); - LOCATING_TIMEOUT = mParser.getLong(KEY_LOCATING_TIMEOUT, + LOCATING_TIMEOUT = mParser.getDurationMillis(KEY_LOCATING_TIMEOUT, !DEBUG ? 30 * 1000L : 15 * 1000L); LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20); - MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT, + MOTION_INACTIVE_TIMEOUT = mParser.getDurationMillis(KEY_MOTION_INACTIVE_TIMEOUT, !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L); long idleAfterInactiveTimeout = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L; - IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT, + IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getDurationMillis( + KEY_IDLE_AFTER_INACTIVE_TIMEOUT, !COMPRESS_TIME ? idleAfterInactiveTimeout : (idleAfterInactiveTimeout / 10)); - IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_IDLE_PENDING_TIMEOUT, + IDLE_PENDING_TIMEOUT = mParser.getDurationMillis(KEY_IDLE_PENDING_TIMEOUT, !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L); - MAX_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_PENDING_TIMEOUT, + MAX_IDLE_PENDING_TIMEOUT = mParser.getDurationMillis(KEY_MAX_IDLE_PENDING_TIMEOUT, !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L); IDLE_PENDING_FACTOR = mParser.getFloat(KEY_IDLE_PENDING_FACTOR, 2f); - IDLE_TIMEOUT = mParser.getLong(KEY_IDLE_TIMEOUT, + IDLE_TIMEOUT = mParser.getDurationMillis(KEY_IDLE_TIMEOUT, !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L); - MAX_IDLE_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_TIMEOUT, + MAX_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_MAX_IDLE_TIMEOUT, !COMPRESS_TIME ? 6 * 60 * 60 * 1000L : 30 * 60 * 1000L); IDLE_FACTOR = mParser.getFloat(KEY_IDLE_FACTOR, 2f); - MIN_TIME_TO_ALARM = mParser.getLong(KEY_MIN_TIME_TO_ALARM, + MIN_TIME_TO_ALARM = mParser.getDurationMillis(KEY_MIN_TIME_TO_ALARM, !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L); - MAX_TEMP_APP_WHITELIST_DURATION = mParser.getLong( + MAX_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis( KEY_MAX_TEMP_APP_WHITELIST_DURATION, 5 * 60 * 1000L); - MMS_TEMP_APP_WHITELIST_DURATION = mParser.getLong( + MMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis( KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L); - SMS_TEMP_APP_WHITELIST_DURATION = mParser.getLong( + SMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis( KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L); - NOTIFICATION_WHITELIST_DURATION = mParser.getLong( + NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis( KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L); } } diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 6174aece3937..8361132f24bc 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -261,6 +261,10 @@ option java_package com.android.server 2756 fstrim_finish (time|2|3) # --------------------------- +# Job scheduler +# --------------------------- +8000 job_deferred_execution (time|2|3) + # AudioService.java # --------------------------- 40000 volume_changed (stream|1), (prev_level|1), (level|1), (max_level|1), (caller|3) diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 989cb886c126..5c098e32045b 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -102,8 +102,14 @@ public class IpSecService extends IIpSecService.Stub { /* Binder context for this service */ private final Context mContext; - /** Should be a never-repeating global ID for resources */ - private static AtomicInteger mNextResourceId = new AtomicInteger(0x00FADED0); + /** + * The next non-repeating global ID for tracking resources between users, this service, + * and kernel data structures. Accessing this variable is not thread safe, so it is + * only read or modified within blocks synchronized on IpSecService.this. We want to + * avoid -1 (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it). + */ + @GuardedBy("IpSecService.this") + private int mNextResourceId = 1; interface IpSecServiceConfiguration { INetd getNetdInstance() throws RemoteException; @@ -856,7 +862,7 @@ public class IpSecService extends IIpSecService.Stub { checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex"); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); - int resourceId = mNextResourceId.getAndIncrement(); + final int resourceId = mNextResourceId++; int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; String localAddress = ""; @@ -979,7 +985,7 @@ public class IpSecService extends IIpSecService.Stub { int callingUid = Binder.getCallingUid(); UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid); - int resourceId = mNextResourceId.getAndIncrement(); + final int resourceId = mNextResourceId++; FileDescriptor sockFd = null; try { if (!userRecord.mSocketQuotaTracker.isAvailable()) { @@ -1102,7 +1108,7 @@ public class IpSecService extends IIpSecService.Stub { IpSecConfig c, IBinder binder) throws RemoteException { checkIpSecConfig(c); checkNotNull(binder, "Null Binder passed to createTransportModeTransform"); - int resourceId = mNextResourceId.getAndIncrement(); + final int resourceId = mNextResourceId++; UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 57c992fdcf48..ea748db159e1 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -2254,6 +2254,64 @@ public class LocationManagerService extends ILocationManager.Stub { } } + /** + * Provides an interface to inject and set the last location if location is not available + * currently. + * + * This helps in cases where the product (Cars for example) has saved the last known location + * before powering off. This interface lets the client inject the saved location while the GPS + * chipset is getting its first fix, there by improving user experience. + * + * @param location - Location object to inject + * @return true if update was successful, false if not + */ + @Override + public boolean injectLocation(Location location) { + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, + "Location Hardware permission not granted to inject location"); + mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, + "Access Fine Location permission not granted to inject Location"); + + if (location == null) { + if (D) { + Log.d(TAG, "injectLocation(): called with null location"); + } + return false; + } + LocationProviderInterface p = null; + String provider = location.getProvider(); + if (provider != null) { + p = mProvidersByName.get(provider); + } + if (p == null) { + if (D) { + Log.d(TAG, "injectLocation(): unknown provider"); + } + return false; + } + synchronized (mLock) { + if (!isAllowedByCurrentUserSettingsLocked(provider)) { + if (D) { + Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId); + } + return false; + } else { + // NOTE: If last location is already available, location is not injected. If + // provider's normal source (like a GPS chipset) have already provided an output, + // there is no need to inject this location. + if (mLastLocation.get(provider) == null) { + updateLastLocationLocked(location, provider); + } else { + if (D) { + Log.d(TAG, "injectLocation(): Location exists. Not updating"); + } + return false; + } + } + } + return true; + } + @Override public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent, String packageName) { @@ -2614,30 +2672,18 @@ public class LocationManagerService extends ILocationManager.Stub { private void handleLocationChangedLocked(Location location, boolean passive) { if (D) Log.d(TAG, "incoming location: " + location); - long now = SystemClock.elapsedRealtime(); String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider()); - // Skip if the provider is unknown. LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) return; - - // Update last known locations - Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); - Location lastNoGPSLocation; + updateLastLocationLocked(location, provider); + // mLastLocation should have been updated from the updateLastLocationLocked call above. Location lastLocation = mLastLocation.get(provider); if (lastLocation == null) { - lastLocation = new Location(provider); - mLastLocation.put(provider, lastLocation); - } else { - lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); - if (noGPSLocation == null && lastNoGPSLocation != null) { - // New location has no no-GPS location: adopt last no-GPS location. This is set - // directly into location because we do not want to notify COARSE clients. - location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation); - } + Log.e(TAG, "handleLocationChangedLocked() updateLastLocation failed"); + return; } - lastLocation.set(location); // Update last known coarse interval location if enough time has passed. Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider); @@ -2653,7 +2699,7 @@ public class LocationManagerService extends ILocationManager.Stub { // Don't ever return a coarse location that is more recent than the allowed update // interval (i.e. don't allow an app to keep registering and unregistering for // location updates to overcome the minimum interval). - noGPSLocation = + Location noGPSLocation = lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); // Skip if there are no UpdateRecords for this provider. @@ -2778,6 +2824,30 @@ public class LocationManagerService extends ILocationManager.Stub { } } + /** + * Updates last location with the given location + * + * @param location new location to update + * @param provider Location provider to update for + */ + private void updateLastLocationLocked(Location location, String provider) { + Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); + Location lastNoGPSLocation; + Location lastLocation = mLastLocation.get(provider); + if (lastLocation == null) { + lastLocation = new Location(provider); + mLastLocation.put(provider, lastLocation); + } else { + lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); + if (noGPSLocation == null && lastNoGPSLocation != null) { + // New location has no no-GPS location: adopt last no-GPS location. This is set + // directly into location because we do not want to notify COARSE clients. + location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation); + } + } + lastLocation.set(location); + } + private class LocationWorkerHandler extends Handler { public LocationWorkerHandler(Looper looper) { super(looper, null, true); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 8b13aa052da0..dc2f2a5f53d5 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -2191,7 +2191,7 @@ class StorageManagerService extends IStorageManager.Stub } try { - mVold.fdeEnable(type, password, IVold.ENCRYPTION_FLAG_IN_PLACE); + mVold.fdeEnable(type, password, 0); } catch (Exception e) { Slog.wtf(TAG, e); return -1; diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 0e51fda0f39f..c1cda985a9ba 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -26,6 +26,7 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.input.InputManager; import android.hardware.vibrator.V1_0.Constants.EffectStrength; +import android.icu.text.DateFormat; import android.media.AudioManager; import android.os.PowerManager.ServiceType; import android.os.PowerSaveState; @@ -62,6 +63,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.LinkedList; +import java.util.Date; public class VibratorService extends IVibratorService.Stub implements InputManager.InputDeviceListener { @@ -69,6 +71,8 @@ public class VibratorService extends IVibratorService.Stub private static final boolean DEBUG = false; private static final String SYSTEM_UI_PACKAGE = "com.android.systemui"; + private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 }; + private final LinkedList<VibrationInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; private final boolean mAllowPriorityVibrationsInLowPowerMode; @@ -110,7 +114,12 @@ public class VibratorService extends IVibratorService.Stub private class Vibration implements IBinder.DeathRecipient { private final IBinder mToken; private final VibrationEffect mEffect; + // Start time in CLOCK_BOOTTIME base. private final long mStartTime; + // Start time in unix epoch time. Only to be used for debugging purposes and to correlate + // with other system events, any duration calculations should be done use mStartTime so as + // not to be affected by discontinuities created by RTC adjustments. + private final long mStartTimeDebug; private final int mUsageHint; private final int mUid; private final String mOpPkg; @@ -119,7 +128,8 @@ public class VibratorService extends IVibratorService.Stub int usageHint, int uid, String opPkg) { mToken = token; mEffect = effect; - mStartTime = SystemClock.uptimeMillis(); + mStartTime = SystemClock.elapsedRealtime(); + mStartTimeDebug = System.currentTimeMillis(); mUsageHint = usageHint; mUid = uid; mOpPkg = opPkg; @@ -153,18 +163,22 @@ public class VibratorService extends IVibratorService.Stub return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg)) && !repeating; } + + public VibrationInfo toInfo() { + return new VibrationInfo(mStartTimeDebug, mEffect, mUsageHint, mUid, mOpPkg); + } } private static class VibrationInfo { - private final long mStartTime; + private final long mStartTimeDebug; private final VibrationEffect mEffect; private final int mUsageHint; private final int mUid; private final String mOpPkg; - public VibrationInfo(long startTime, VibrationEffect effect, + public VibrationInfo(long startTimeDebug, VibrationEffect effect, int usageHint, int uid, String opPkg) { - mStartTime = startTime; + mStartTimeDebug = startTimeDebug; mEffect = effect; mUsageHint = usageHint; mUid = uid; @@ -174,8 +188,8 @@ public class VibratorService extends IVibratorService.Stub @Override public String toString() { return new StringBuilder() - .append(", startTime: ") - .append(mStartTime) + .append("startTime: ") + .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug))) .append(", effect: ") .append(mEffect) .append(", usageHint: ") @@ -225,7 +239,7 @@ public class VibratorService extends IVibratorService.Stub com.android.internal.R.array.config_virtualKeyVibePattern); VibrationEffect clickEffect = createEffect(clickEffectTimings); VibrationEffect doubleClickEffect = VibrationEffect.createWaveform( - new long[] {0, 30, 100, 30} /*timings*/, -1); + DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/); long[] tickEffectTimings = getLongIntArray(context.getResources(), com.android.internal.R.array.config_clockTickVibePattern); VibrationEffect tickEffect = createEffect(tickEffectTimings); @@ -392,17 +406,7 @@ public class VibratorService extends IVibratorService.Stub } Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg); - - // Only link against waveforms since they potentially don't have a finish if - // they're repeating. Let other effects just play out until they're done. - if (effect instanceof VibrationEffect.Waveform) { - try { - token.linkToDeath(vib, 0); - } catch (RemoteException e) { - return; - } - } - + linkVibration(vib); long ident = Binder.clearCallingIdentity(); try { @@ -430,8 +434,7 @@ public class VibratorService extends IVibratorService.Stub if (mPreviousVibrations.size() > mPreviousVibrationsLimit) { mPreviousVibrations.removeFirst(); } - mPreviousVibrations.addLast(new VibrationInfo( - vib.mStartTime, vib.mEffect, vib.mUsageHint, vib.mUid, vib.mOpPkg)); + mPreviousVibrations.addLast(vib.toInfo()); } @Override // Binder call @@ -589,10 +592,23 @@ public class VibratorService extends IVibratorService.Stub AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid, mCurrentVibration.mOpPkg); } catch (RemoteException e) { } + unlinkVibration(mCurrentVibration); mCurrentVibration = null; } } + private void linkVibration(Vibration vib) { + // Only link against waveforms since they potentially don't have a finish if + // they're repeating. Let other effects just play out until they're done. + if (vib.mEffect instanceof VibrationEffect.Waveform) { + try { + vib.mToken.linkToDeath(vib, 0); + } catch (RemoteException e) { + return; + } + } + } + private void unlinkVibration(Vibration vib) { if (vib.mEffect instanceof VibrationEffect.Waveform) { vib.mToken.unlinkToDeath(vib, 0); @@ -742,8 +758,7 @@ public class VibratorService extends IVibratorService.Stub synchronized (mInputDeviceVibrators) { VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.mEffect; // Input devices don't support prebaked effect, so skip trying it with them. - final int vibratorCount = mInputDeviceVibrators.size(); - if (vibratorCount == 0) { + if (mInputDeviceVibrators.isEmpty()) { long timeout = vibratorPerformEffect(prebaked.getId(), EffectStrength.MEDIUM); if (timeout > 0) { noteVibratorOnLocked(vib.mUid, timeout); @@ -753,12 +768,11 @@ public class VibratorService extends IVibratorService.Stub if (!prebaked.shouldFallback()) { return 0; } - final int id = prebaked.getId(); - if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) { + VibrationEffect effect = getFallbackEffect(prebaked.getId()); + if (effect == null) { Slog.w(TAG, "Failed to play prebaked effect, no fallback"); return 0; } - VibrationEffect effect = mFallbackEffects[id]; Vibration fallbackVib = new Vibration(vib.mToken, effect, vib.mUsageHint, vib.mUid, vib.mOpPkg); startVibrationInnerLocked(fallbackVib); @@ -766,6 +780,13 @@ public class VibratorService extends IVibratorService.Stub return 0; } + private VibrationEffect getFallbackEffect(int effectId) { + if (effectId < 0 || effectId >= mFallbackEffects.length) { + return null; + } + return mFallbackEffects[effectId]; + } + private void noteVibratorOnLocked(int uid, long millis) { try { mBatteryStatsService.noteVibratorOn(uid, millis); diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index af5cf1ee4202..e38148c7bd42 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -556,10 +556,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { return stack == getTopStack(); } - boolean isTopFullscreenStack(ActivityStack stack) { + boolean isTopNotPinnedStack(ActivityStack stack) { for (int i = mStacks.size() - 1; i >= 0; --i) { final ActivityStack current = mStacks.get(i); - if (current.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + if (!current.inPinnedWindowingMode()) { return current == stack; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index b3a596c82dd7..0d6d2bde48c8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -77,8 +77,8 @@ final class ActivityManagerConstants extends ContentObserver { private static final long DEFAULT_CONTENT_PROVIDER_RETAIN_TIME = 20*1000; private static final long DEFAULT_GC_TIMEOUT = 5*1000; private static final long DEFAULT_GC_MIN_INTERVAL = 60*1000; - private static final long DEFAULT_FULL_PSS_MIN_INTERVAL = 10*60*1000; - private static final long DEFAULT_FULL_PSS_LOWERED_INTERVAL = 2*60*1000; + private static final long DEFAULT_FULL_PSS_MIN_INTERVAL = 20*60*1000; + private static final long DEFAULT_FULL_PSS_LOWERED_INTERVAL = 5*60*1000; private static final long DEFAULT_POWER_CHECK_INTERVAL = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000; private static final int DEFAULT_POWER_CHECK_MAX_CPU_1 = 25; private static final int DEFAULT_POWER_CHECK_MAX_CPU_2 = 25; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 81e34df57c11..624035d2c720 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2521,13 +2521,15 @@ public class ActivityManagerService extends IActivityManager.Stub } } if (proc != null) { + long startTime = SystemClock.currentThreadTimeMillis(); long pss = Debug.getPss(pid, tmp, null); + long endTime = SystemClock.currentThreadTimeMillis(); synchronized (ActivityManagerService.this) { if (pss != 0 && proc.thread != null && proc.setProcState == procState && proc.pid == pid && proc.lastPssTime == lastPssTime) { num++; recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], - SystemClock.uptimeMillis()); + endTime-startTime, SystemClock.uptimeMillis()); } } } @@ -2695,6 +2697,13 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public void onBootPhase(int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + mService.mBatteryStatsService.systemServicesReady(); + } + } + + @Override public void onCleanupUser(int userId) { mService.mBatteryStatsService.onCleanupUser(userId); } @@ -6539,13 +6548,17 @@ public class ActivityManagerService extends IActivityManager.Stub } } infos[i] = new Debug.MemoryInfo(); + long startTime = SystemClock.currentThreadTimeMillis(); Debug.getMemoryInfo(pids[i], infos[i]); + long endTime = SystemClock.currentThreadTimeMillis(); if (proc != null) { synchronized (this) { if (proc.thread != null && proc.setAdj == oomAdj) { // Record this for posterity if the process has been stable. proc.baseProcessTracker.addPss(infos[i].getTotalPss(), - infos[i].getTotalUss(), false, proc.pkgList); + infos[i].getTotalUss(), false, + ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime, + proc.pkgList); } } } @@ -6567,12 +6580,15 @@ public class ActivityManagerService extends IActivityManager.Stub } } long[] tmpUss = new long[1]; + long startTime = SystemClock.currentThreadTimeMillis(); pss[i] = Debug.getPss(pids[i], tmpUss, null); + long endTime = SystemClock.currentThreadTimeMillis(); if (proc != null) { synchronized (this) { if (proc.thread != null && proc.setAdj == oomAdj) { // Record this for posterity if the process has been stable. - proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false, proc.pkgList); + proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false, + ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList); } } } @@ -17661,11 +17677,20 @@ public class ActivityManagerService extends IActivityManager.Stub if (mi == null) { mi = new Debug.MemoryInfo(); } + final int reportType; + final long startTime; + final long endTime; if (opts.dumpDetails || (!brief && !opts.oomOnly)) { + reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW; + startTime = SystemClock.currentThreadTimeMillis(); Debug.getMemoryInfo(pid, mi); + endTime = SystemClock.currentThreadTimeMillis(); hasSwapPss = mi.hasSwappedOutPss; } else { + reportType = ProcessStats.ADD_PSS_EXTERNAL; + startTime = SystemClock.currentThreadTimeMillis(); mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null); + endTime = SystemClock.currentThreadTimeMillis(); mi.dalvikPrivateDirty = (int)tmpLong[0]; } if (opts.dumpDetails) { @@ -17708,7 +17733,8 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. - r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, r.pkgList); + r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, + reportType, endTime-startTime, r.pkgList); } } @@ -18151,11 +18177,20 @@ public class ActivityManagerService extends IActivityManager.Stub if (mi == null) { mi = new Debug.MemoryInfo(); } + final int reportType; + final long startTime; + final long endTime; if (opts.dumpDetails || (!brief && !opts.oomOnly)) { + reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW; + startTime = SystemClock.currentThreadTimeMillis(); Debug.getMemoryInfo(pid, mi); + endTime = SystemClock.currentThreadTimeMillis(); hasSwapPss = mi.hasSwappedOutPss; } else { + reportType = ProcessStats.ADD_PSS_EXTERNAL; + startTime = SystemClock.currentThreadTimeMillis(); mi.dalvikPss = (int) Debug.getPss(pid, tmpLong, null); + endTime = SystemClock.currentThreadTimeMillis(); mi.dalvikPrivateDirty = (int) tmpLong[0]; } if (opts.dumpDetails) { @@ -18194,7 +18229,8 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. - r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, r.pkgList); + r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, + reportType, endTime-startTime, r.pkgList); } } @@ -22456,11 +22492,12 @@ public class ActivityManagerService extends IActivityManager.Stub * Record new PSS sample for a process. */ void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss, - long now) { + long pssDuration, long now) { EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024, swapPss * 1024); proc.lastPssTime = now; - proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList); + proc.baseProcessTracker.addPss(pss, uss, true, ProcessStats.ADD_PSS_INTERNAL, + pssDuration, proc.pkgList); if (DEBUG_PSS) Slog.d(TAG_PSS, "PSS of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss + " state=" + ProcessList.makeProcStateString(procState)); @@ -22955,8 +22992,11 @@ public class ActivityManagerService extends IActivityManager.Stub // the data right when a process is transitioning between process // states, which well tend to give noisy data. long start = SystemClock.uptimeMillis(); + long startTime = SystemClock.currentThreadTimeMillis(); long pss = Debug.getPss(app.pid, mTmpLong, null); - recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1], now); + long endTime = SystemClock.currentThreadTimeMillis(); + recordPssSampleLocked(app, app.curProcState, pss, endTime-startTime, + mTmpLong[0], mTmpLong[1], now); mPendingPssProcesses.remove(app); Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState + " to " + app.curProcState + ": " diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 8eb519797641..e1907d37fdf2 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -2730,12 +2730,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } /** - * @return true if the activity contains windows that have - * {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity has set - * {@link #mShowWhenLocked}. + * @return true if the activity windowing mode is not + * {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and activity contains + * windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity + * has set {@link #mShowWhenLocked}. + * Multi-windowing mode will be exited if true is returned. */ boolean canShowWhenLocked() { - return !inMultiWindowMode() && (mShowWhenLocked + return !inPinnedWindowingMode() && (mShowWhenLocked || service.mWindowManager.containsShowWhenLockedWindow(appToken)); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 10c801da7c03..831b31ed89c2 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -16,7 +16,6 @@ package com.android.server.am; -import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -144,7 +143,6 @@ import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService.ItemMatcher; -import com.android.server.am.EventLogTags; import com.android.server.wm.ConfigurationContainer; import com.android.server.wm.StackWindowController; import com.android.server.wm.StackWindowListener; @@ -1013,6 +1011,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } + /** + * The intent behind moving a primary split screen stack to the back is usually to hide + * behind the home stack. Exit split screen in this case. + */ + if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } + getDisplay().positionChildAtBottom(this); mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack()); if (task != null) { @@ -1811,7 +1817,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean behindFullscreenActivity = !stackShouldBeVisible; boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this) && (isInStackLocked(starting) == null); - final boolean isTopFullscreenStack = getDisplay().isTopFullscreenStack(this); + final boolean isTopNotPinnedStack = getDisplay().isTopNotPinnedStack(this); for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); final ArrayList<ActivityRecord> activities = task.mActivities; @@ -1833,7 +1839,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Now check whether it's really visible depending on Keyguard state. final boolean reallyVisible = checkKeyguardVisibility(r, - visibleIgnoringKeyguard, isTop && isTopFullscreenStack); + visibleIgnoringKeyguard, isTop && isTopNotPinnedStack); if (visibleIgnoringKeyguard) { behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible, behindFullscreenActivity, r); diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 45824309eefb..1fcaeef72dba 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -35,6 +35,7 @@ import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.util.function.pooled.PooledLambda; import libcore.util.EmptyArray; @@ -117,49 +118,45 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } @Override - public Future<?> scheduleReadProcStateCpuTimes() { + public synchronized Future<?> scheduleCpuSyncDueToSettingChange() { + return scheduleSyncLocked("setting-change", UPDATE_CPU); + } + + @Override + public Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff) { synchronized (mStats) { - if (!mStats.mPerProcStateCpuTimesAvailable) { + if (!mStats.trackPerProcStateCpuTimes()) { return null; } } synchronized (BatteryExternalStatsWorker.this) { if (!mExecutorService.isShutdown()) { - return mExecutorService.submit(mReadProcStateCpuTimesTask); + return mExecutorService.submit(PooledLambda.obtainRunnable( + BatteryStatsImpl::updateProcStateCpuTimes, + mStats, onBattery, onBatteryScreenOff).recycleOnUse()); } } return null; } @Override - public Future<?> scheduleCopyFromAllUidsCpuTimes() { + public Future<?> scheduleCopyFromAllUidsCpuTimes( + boolean onBattery, boolean onBatteryScreenOff) { synchronized (mStats) { - if (!mStats.mPerProcStateCpuTimesAvailable) { + if (!mStats.trackPerProcStateCpuTimes()) { return null; } } synchronized (BatteryExternalStatsWorker.this) { if (!mExecutorService.isShutdown()) { - return mExecutorService.submit(mCopyFromAllUidsCpuTimesTask); + return mExecutorService.submit(PooledLambda.obtainRunnable( + BatteryStatsImpl::copyFromAllUidsCpuTimes, + mStats, onBattery, onBatteryScreenOff).recycleOnUse()); } } return null; } - private final Runnable mReadProcStateCpuTimesTask = new Runnable() { - @Override - public void run() { - mStats.updateProcStateCpuTimes(); - } - }; - - private final Runnable mCopyFromAllUidsCpuTimesTask = new Runnable() { - @Override - public void run() { - mStats.copyFromAllUidsCpuTimes(); - } - }; - public synchronized Future<?> scheduleWrite() { if (mExecutorService.isShutdown()) { return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown")); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 813e617cc2e0..c9aa9a2e2fa3 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -185,6 +185,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder()); } + public void systemServicesReady() { + mStats.systemServicesReady(mContext); + } + private final class LocalService extends BatteryStatsInternal { @Override public String[] getWifiIfaces() { @@ -1185,6 +1189,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub pw.println(" --write: force write current collected stats to disk."); pw.println(" --new-daily: immediately create and write new daily stats record."); pw.println(" --read-daily: read-load last written daily stats."); + pw.println(" --settings: dump the settings key/values related to batterystats"); pw.println(" <package.name>: optional name of package to filter output by."); pw.println(" -h: print this help text."); pw.println("Battery stats (batterystats) commands:"); @@ -1197,6 +1202,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub pw.println(" pretend-screen-off: pretend the screen is off, even if screen state changes"); } + private void dumpSettings(PrintWriter pw) { + synchronized (mStats) { + mStats.dumpConstantsLocked(pw); + } + } + private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) { i++; if (i >= args.length) { @@ -1307,6 +1318,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub } else if ("-h".equals(arg)) { dumpHelp(pw); return; + } else if ("--settings".equals(arg)) { + dumpSettings(pw); + return; } else if ("-a".equals(arg)) { flags |= BatteryStats.DUMP_VERBOSE; } else if (arg.length() > 0 && arg.charAt(0) == '-'){ diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index ab5d64c48ac8..b39e96df7949 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -432,13 +432,13 @@ public final class ProcessList { public static final int PSS_MIN_TIME_FROM_STATE_CHANGE = 15*1000; // The maximum amount of time we want to go between PSS collections. - public static final int PSS_MAX_INTERVAL = 30*60*1000; + public static final int PSS_MAX_INTERVAL = 40*60*1000; // The minimum amount of time between successive PSS requests for *all* processes. - public static final int PSS_ALL_INTERVAL = 10*60*1000; + public static final int PSS_ALL_INTERVAL = 20*60*1000; - // The minimum amount of time between successive PSS requests for a process. - private static final int PSS_SHORT_INTERVAL = 2*60*1000; + // The amount of time until PSS when a persistent process first appears. + private static final int PSS_FIRST_PERSISTENT_INTERVAL = 30*1000; // The amount of time until PSS when a process first becomes top. private static final int PSS_FIRST_TOP_INTERVAL = 10*1000; @@ -449,6 +449,9 @@ public final class ProcessList { // The amount of time until PSS when a process first becomes cached. private static final int PSS_FIRST_CACHED_INTERVAL = 30*1000; + // The amount of time until PSS when the top process stays in the same state. + private static final int PSS_SAME_TOP_INTERVAL = 5*60*1000; + // The amount of time until PSS when an important process stays in the same state. private static final int PSS_SAME_IMPORTANT_INTERVAL = 15*60*1000; @@ -458,6 +461,18 @@ public final class ProcessList { // The amount of time until PSS when a cached process stays in the same state. private static final int PSS_SAME_CACHED_INTERVAL = 30*60*1000; + // The amount of time until PSS when a persistent process first appears. + private static final int PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL = 1*60*1000; + + // The amount of time until PSS when a process first becomes top. + private static final int PSS_FIRST_ASLEEP_TOP_INTERVAL = 20*1000; + + // The amount of time until PSS when a process first goes into the background. + private static final int PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL = 30*1000; + + // The amount of time until PSS when a process first becomes cached. + private static final int PSS_FIRST_ASLEEP_CACHED_INTERVAL = 1*60*1000; + // The minimum time interval after a state change it is safe to collect PSS. public static final int PSS_TEST_MIN_TIME_FROM_STATE_CHANGE = 10*1000; @@ -502,8 +517,8 @@ public final class ProcessList { }; private static final long[] sFirstAwakePssTimes = new long[] { - PSS_SHORT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT - PSS_SHORT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT_UI + PSS_FIRST_PERSISTENT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT + PSS_FIRST_PERSISTENT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT_UI PSS_FIRST_TOP_INTERVAL, // ActivityManager.PROCESS_STATE_TOP PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE @@ -526,7 +541,51 @@ public final class ProcessList { private static final long[] sSameAwakePssTimes = new long[] { PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT_UI - PSS_SHORT_INTERVAL, // ActivityManager.PROCESS_STATE_TOP + PSS_SAME_TOP_INTERVAL, // ActivityManager.PROCESS_STATE_TOP + PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE + PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE + PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND + PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND + PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_BACKUP + PSS_SAME_SERVICE_INTERVAL, // ActivityManager.PROCESS_STATE_SERVICE + PSS_SAME_SERVICE_INTERVAL, // ActivityManager.PROCESS_STATE_RECEIVER + PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING + PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT + PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_HOME + PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY + PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY + PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT + PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_RECENT + PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_EMPTY + }; + + private static final long[] sFirstAsleepPssTimes = new long[] { + PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT + PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT_UI + PSS_FIRST_ASLEEP_TOP_INTERVAL, // ActivityManager.PROCESS_STATE_TOP + PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE + PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE + PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND + PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND + PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BACKUP + PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_SERVICE + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_RECEIVER + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_HOME + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_RECENT + PSS_FIRST_ASLEEP_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_EMPTY + }; + + private static final long[] sSameAsleepPssTimes = new long[] { + PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT + PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT_UI + PSS_SAME_TOP_INTERVAL, // ActivityManager.PROCESS_STATE_TOP PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND @@ -604,8 +663,8 @@ public final class ProcessList { ? sTestFirstPssTimes : sTestSamePssTimes) : (first - ? sFirstAwakePssTimes - : sSameAwakePssTimes); + ? (sleeping ? sFirstAsleepPssTimes : sFirstAwakePssTimes) + : (sleeping ? sSameAsleepPssTimes : sSameAwakePssTimes)); return now + table[procState]; } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index bcb57efffc19..70e2c8d076cc 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -185,6 +185,11 @@ public final class JobSchedulerService extends com.android.server.SystemService boolean mReportedActive; /** + * Are we currently in device-wide standby parole? + */ + volatile boolean mInParole; + + /** * Current limit on the number of concurrent JobServiceContext entries we want to * keep actively running a job. */ @@ -466,11 +471,11 @@ public final class JobSchedulerService extends com.android.server.SystemService DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT); MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT, DEFAULT_MAX_WORK_RESCHEDULE_COUNT); - MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME, + MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME, DEFAULT_MIN_LINEAR_BACKOFF_TIME); - MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME, + MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME, DEFAULT_MIN_EXP_BACKOFF_TIME); - STANDBY_HEARTBEAT_TIME = mParser.getLong(KEY_STANDBY_HEARTBEAT_TIME, + STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME, DEFAULT_STANDBY_HEARTBEAT_TIME); STANDBY_BEATS[1] = mParser.getInt(KEY_STANDBY_WORKING_BEATS, DEFAULT_STANDBY_WORKING_BEATS); @@ -1720,28 +1725,31 @@ public final class JobSchedulerService extends com.android.server.SystemService } // If the app is in a non-active standby bucket, make sure we've waited - // an appropriate amount of time since the last invocation - final int bucket = job.getStandbyBucket(); - if (mHeartbeat < mNextBucketHeartbeat[bucket]) { - // Only skip this job if it's still waiting for the end of its (initial) nominal - // bucket interval. Once it's waited that long, we let it go ahead and clear. - // The final (NEVER) bucket is special; we never age those apps' jobs into - // runnability. - if (bucket >= mConstants.STANDBY_BEATS.length - || (mHeartbeat < job.getBaseHeartbeat() + mConstants.STANDBY_BEATS[bucket])) { - // TODO: log/trace that we're deferring the job due to bucketing if we hit this - if (job.getWhenStandbyDeferred() == 0) { + // an appropriate amount of time since the last invocation. During device- + // wide parole, standby bucketing is ignored. + if (!mInParole) { + final int bucket = job.getStandbyBucket(); + if (mHeartbeat < mNextBucketHeartbeat[bucket]) { + // Only skip this job if it's still waiting for the end of its (initial) nominal + // bucket interval. Once it's waited that long, we let it go ahead and clear. + // The final (NEVER) bucket is special; we never age those apps' jobs into + // runnability. + if (bucket >= mConstants.STANDBY_BEATS.length + || (mHeartbeat < job.getBaseHeartbeat() + mConstants.STANDBY_BEATS[bucket])) { + // TODO: log/trace that we're deferring the job due to bucketing if we hit this + if (job.getWhenStandbyDeferred() == 0) { + if (DEBUG_STANDBY) { + Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < " + + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job); + } + job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); + } + return false; + } else { if (DEBUG_STANDBY) { - Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < " - + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job); + Slog.v(TAG, "Bucket deferred job aged into runnability at " + + mHeartbeat + " : " + job); } - job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); - } - return false; - } else { - if (DEBUG_STANDBY) { - Slog.v(TAG, "Bucket deferred job aged into runnability at " - + mHeartbeat + " : " + job); } } } @@ -2086,7 +2094,10 @@ public final class JobSchedulerService extends com.android.server.SystemService @Override public void onParoleStateChanged(boolean isParoleOn) { - // Unused + if (DEBUG_STANDBY) { + Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF")); + } + mInParole = isParoleOn; } } diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index 6a3fd04a4e26..709deeb81ac9 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -38,12 +38,14 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; import android.os.WorkSource; +import android.util.EventLog; import android.util.Slog; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; +import com.android.server.EventLogTags; import com.android.server.job.controllers.JobStatus; /** @@ -222,17 +224,20 @@ public final class JobServiceContext implements ServiceConnection { isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network); mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis(); - if (DEBUG_STANDBY) { - final long whenDeferred = job.getWhenStandbyDeferred(); - if (whenDeferred > 0) { + final long whenDeferred = job.getWhenStandbyDeferred(); + if (whenDeferred > 0) { + final long deferral = mExecutionStartTimeElapsed - whenDeferred; + EventLog.writeEvent(EventLogTags.JOB_DEFERRED_EXECUTION, deferral); + if (DEBUG_STANDBY) { StringBuilder sb = new StringBuilder(128); sb.append("Starting job deferred for standby by "); - TimeUtils.formatDuration(mExecutionStartTimeElapsed - whenDeferred, sb); - sb.append(" : "); + TimeUtils.formatDuration(deferral, sb); + sb.append(" ms : "); sb.append(job.toShortString()); Slog.v(TAG, sb.toString()); } } + // Once we'e begun executing a job, we by definition no longer care whether // it was inflated from disk with not-yet-coherent delay/deadline bounds. job.clearPersistedUtcTimes(); diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index c964f912feb1..af20cd77e626 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -49,7 +49,6 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; -import com.android.server.pm.permission.BasePermission; import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; @@ -302,7 +301,7 @@ class InstantAppRegistry { // into account but also allow the value from the old computation to avoid // data loss. final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests( - pkg.mSignatures); + pkg.mSigningDetails.signatures); final String signaturesSha256Digest = PackageUtils.computeSignaturesSha256Digest( signaturesSha256Digests); @@ -313,7 +312,7 @@ class InstantAppRegistry { } // For backwards compatibility we accept match based on first signature - if (pkg.mSignatures.length > 1 && currentCookieFile.equals(computeInstantCookieFile( + if (pkg.mSigningDetails.signatures.length > 1 && currentCookieFile.equals(computeInstantCookieFile( pkg.packageName, signaturesSha256Digests[0], userId))) { return; } @@ -1176,12 +1175,13 @@ class InstantAppRegistry { // We prefer the modern computation procedure where all certs are taken // into account and delete the file derived via the legacy hash computation. File newCookieFile = computeInstantCookieFile(pkg.packageName, - PackageUtils.computeSignaturesSha256Digest(pkg.mSignatures), userId); - if (pkg.mSignatures.length > 0) { - File oldCookieFile = peekInstantCookieFile(pkg.packageName, userId); - if (oldCookieFile != null && !newCookieFile.equals(oldCookieFile)) { - oldCookieFile.delete(); - } + PackageUtils.computeSignaturesSha256Digest(pkg.mSigningDetails.signatures), userId); + if (!pkg.mSigningDetails.hasSignatures()) { + Slog.wtf(LOG_TAG, "Parsed Instant App contains no valid signatures!"); + } + File oldCookieFile = peekInstantCookieFile(pkg.packageName, userId); + if (oldCookieFile != null && !newCookieFile.equals(oldCookieFile)) { + oldCookieFile.delete(); } cancelPendingPersistLPw(pkg, userId); addPendingPersistCookieLPw(userId, pkg, cookie, newCookieFile); diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java index fca95857c7e0..93d3b77511bc 100644 --- a/services/core/java/com/android/server/pm/KeySetManagerService.java +++ b/services/core/java/com/android/server/pm/KeySetManagerService.java @@ -188,7 +188,7 @@ public class KeySetManagerService { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Passed invalid package to keyset validation."); } - ArraySet<PublicKey> signingKeys = pkg.mSigningKeys; + ArraySet<PublicKey> signingKeys = pkg.mSigningDetails.publicKeys; if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Package has invalid signing-key-set."); @@ -223,7 +223,7 @@ public class KeySetManagerService { PackageSetting ps = mPackages.get(pkg.packageName); Preconditions.checkNotNull(ps, "pkg: " + pkg.packageName + "does not have a corresponding entry in mPackages."); - addSigningKeySetToPackageLPw(ps, pkg.mSigningKeys); + addSigningKeySetToPackageLPw(ps, pkg.mSigningDetails.publicKeys); if (pkg.mKeySetMapping != null) { addDefinedKeySetsToPackageLPw(ps, pkg.mKeySetMapping); if (pkg.mUpgradeKeySets != null) { @@ -371,7 +371,7 @@ public class KeySetManagerService { long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets(); for (int i = 0; i < upgradeKeySets.length; i++) { Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]); - if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) { + if (upgradeSet != null && newPkg.mSigningDetails.publicKeys.containsAll(upgradeSet)) { return true; } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 5577de8ccde9..4e918983ddd6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -58,7 +58,6 @@ import android.content.pm.PackageParser; import android.content.pm.PackageParser.ApkLite; import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; -import android.content.pm.Signature; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Binder; @@ -84,6 +83,7 @@ import android.util.ArraySet; import android.util.ExceptionUtils; import android.util.MathUtils; import android.util.Slog; +import android.util.apk.ApkSignatureVerifier; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.NativeLibraryHelper; @@ -107,7 +107,7 @@ import java.io.FileDescriptor; import java.io.FileFilter; import java.io.FileOutputStream; import java.io.IOException; -import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -227,9 +227,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private long mVersionCode; @GuardedBy("mLock") - private Signature[] mSignatures; - @GuardedBy("mLock") - private Certificate[][] mCertificates; + private PackageParser.SigningDetails mSigningDetails; /** * Path to the validated base APK for this session, which may point at an @@ -857,7 +855,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } Preconditions.checkNotNull(mPackageName); - Preconditions.checkNotNull(mSignatures); + Preconditions.checkNotNull(mSigningDetails); Preconditions.checkNotNull(mResolvedBaseFile); if (needToAskForPermissionsLocked()) { @@ -938,7 +936,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mRelinquished = true; mPm.installStage(mPackageName, stageDir, localObserver, params, - mInstallerPackageName, mInstallerUid, user, mCertificates); + mInstallerPackageName, mInstallerUid, user, mSigningDetails); } /** @@ -957,7 +955,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throws PackageManagerException { mPackageName = null; mVersionCode = -1; - mSignatures = null; + mSigningDetails = PackageParser.SigningDetails.UNKNOWN; mResolvedBaseFile = null; mResolvedStagedFiles.clear(); @@ -1009,9 +1007,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mPackageName = apk.packageName; mVersionCode = apk.getLongVersionCode(); } - if (mSignatures == null) { - mSignatures = apk.signatures; - mCertificates = apk.certificates; + if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { + mSigningDetails = apk.signingDetails; } assertApkConsistentLocked(String.valueOf(addedFile), apk); @@ -1060,8 +1057,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mPackageName = pkgInfo.packageName; mVersionCode = pkgInfo.getLongVersionCode(); } - if (mSignatures == null) { - mSignatures = pkgInfo.signatures; + if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) { + try { + mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts( + pkgInfo.applicationInfo.sourceDir, + PackageParser.SigningDetails.SignatureSchemeVersion.JAR); + } catch (PackageParserException e) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Couldn't obtain signatures from base APK"); + } } } @@ -1155,7 +1159,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + " version code " + apk.versionCode + " inconsistent with " + mVersionCode); } - if (!Signature.areExactMatch(mSignatures, apk.signatures)) { + if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " signatures are inconsistent"); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 44aad4405a1b..3df7c47e16d7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -337,7 +337,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SecureRandom; -import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; @@ -5360,7 +5359,7 @@ public class PackageManagerService extends IPackageManager.Stub || filterAppAccessLPr(ps2, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } - return compareSignatures(p1.mSignatures, p2.mSignatures); + return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures); } } @@ -8194,19 +8193,11 @@ public class PackageManagerService extends IPackageManager.Stub && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(pkg) && !isRecoverSignatureUpdateNeeded(pkg)) { - long mSigningKeySetId = ps.keySetData.getProperSigningKeySet(); - final KeySetManagerService ksms = mSettings.mKeySetManagerService; - ArraySet<PublicKey> signingKs; - synchronized (mPackages) { - signingKs = ksms.getPublicKeysFromKeySetLPr(mSigningKeySetId); - } - if (ps.signatures.mSignatures != null - && ps.signatures.mSignatures.length != 0 - && signingKs != null) { - // Optimization: reuse the existing cached certificates + if ((ps.pkg != null) && + PackageParser.SigningDetails.UNKNOWN != ps.pkg.mSigningDetails) { + // Optimization: reuse the existing cached signing data // if the package appears to be unchanged. - pkg.mSignatures = ps.signatures.mSignatures; - pkg.mSigningKeys = signingKs; + pkg.mSigningDetails = ps.pkg.mSigningDetails; return; } @@ -8546,7 +8537,7 @@ public class PackageManagerService extends IPackageManager.Stub * Check to make sure the signatures match first. If they don't, * wipe the installed application and its data. */ - if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures) + if (compareSignatures(ps.signatures.mSignatures, pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) { logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but" + " signatures don't match existing userdata copy; removing"); @@ -9518,9 +9509,10 @@ public class PackageManagerService extends IPackageManager.Stub final String[] expectedCertDigests = requiredCertDigests[i]; // For apps targeting O MR1 we require explicit enumeration of all certs. final String[] libCertDigests = (targetSdk > Build.VERSION_CODES.O) - ? PackageUtils.computeSignaturesSha256Digests(libPkg.mSignatures) + ? PackageUtils.computeSignaturesSha256Digests( + libPkg.mSigningDetails.signatures) : PackageUtils.computeSignaturesSha256Digests( - new Signature[]{libPkg.mSignatures[0]}); + new Signature[]{libPkg.mSigningDetails.signatures[0]}); // Take a shortcut if sizes don't match. Note that if an app doesn't // target O we don't parse the "additional-certificate" tags similarly @@ -9856,14 +9848,14 @@ public class PackageManagerService extends IPackageManager.Stub if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) { // We just determined the app is signed correctly, so bring // over the latest parsed certs. - pkgSetting.signatures.mSignatures = pkg.mSignatures; + pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures; } else { if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + pkg.packageName + " upgrade keys do not match the " + "previously installed version"); } else { - pkgSetting.signatures.mSignatures = pkg.mSignatures; + pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures; String msg = "System package " + pkg.packageName + " signature changed; retaining data."; reportSettingsProblem(Log.WARN, msg); @@ -9873,7 +9865,8 @@ public class PackageManagerService extends IPackageManager.Stub try { final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg); final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg); - final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures, + final boolean compatMatch = verifySignatures(signatureCheckPs, + pkg.mSigningDetails.signatures, compareCompat, compareRecover); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { @@ -9883,14 +9876,14 @@ public class PackageManagerService extends IPackageManager.Stub } // We just determined the app is signed correctly, so bring // over the latest parsed certs. - pkgSetting.signatures.mSignatures = pkg.mSignatures; + pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures; } catch (PackageManagerException e) { if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { throw e; } // The signature has changed, but this package is in the system // image... let's recover! - pkgSetting.signatures.mSignatures = pkg.mSignatures; + pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures; // However... if this package is part of a shared user, but it // doesn't match the signature of the shared user, let's fail. // What this means is that you can't change the signatures @@ -9898,7 +9891,7 @@ public class PackageManagerService extends IPackageManager.Stub // that unreasonable. if (signatureCheckPs.sharedUser != null) { if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures, - pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { + pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) { throw new PackageManagerException( INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Signature mismatch for shared user: " @@ -13204,7 +13197,7 @@ public class PackageManagerService extends IPackageManager.Stub void installStage(String packageName, File stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, - Certificate[][] certificates) { + PackageParser.SigningDetails signingDetails) { if (DEBUG_EPHEMERAL) { if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { Slog.d(TAG, "Ephemeral install of " + packageName); @@ -13222,7 +13215,7 @@ public class PackageManagerService extends IPackageManager.Stub final InstallParams params = new InstallParams(origin, null, observer, sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid, verificationInfo, user, sessionParams.abiOverride, - sessionParams.grantedRuntimePermissions, certificates, installReason); + sessionParams.grantedRuntimePermissions, signingDetails, installReason); params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; @@ -13826,7 +13819,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName); if (pkg == null) { return -1; - } else if (pkg.mSignatures.length != 1) { + } else if (pkg.mSigningDetails.signatures.length != 1) { Slog.i(TAG, "Verifier package " + verifierInfo.packageName + " has more than one signature; ignoring"); return -1; @@ -13840,7 +13833,7 @@ public class PackageManagerService extends IPackageManager.Stub final byte[] expectedPublicKey; try { - final Signature verifierSig = pkg.mSignatures[0]; + final Signature verifierSig = pkg.mSigningDetails.signatures[0]; final PublicKey publicKey = verifierSig.getPublicKey(); expectedPublicKey = publicKey.getEncoded(); } catch (CertificateException e) { @@ -14532,13 +14525,13 @@ public class PackageManagerService extends IPackageManager.Stub final String packageAbiOverride; final String[] grantedRuntimePermissions; final VerificationInfo verificationInfo; - final Certificate[][] certificates; + final PackageParser.SigningDetails signingDetails; final int installReason; InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, String volumeUuid, VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride, - String[] grantedPermissions, Certificate[][] certificates, int installReason) { + String[] grantedPermissions, PackageParser.SigningDetails signingDetails, int installReason) { super(user); this.origin = origin; this.move = move; @@ -14549,7 +14542,7 @@ public class PackageManagerService extends IPackageManager.Stub this.verificationInfo = verificationInfo; this.packageAbiOverride = packageAbiOverride; this.grantedRuntimePermissions = grantedPermissions; - this.certificates = certificates; + this.signingDetails = signingDetails; this.installReason = installReason; } @@ -14980,7 +14973,7 @@ public class PackageManagerService extends IPackageManager.Stub /** If non-null, drop an async trace when the install completes */ final String traceMethod; final int traceCookie; - final Certificate[][] certificates; + final PackageParser.SigningDetails signingDetails; final int installReason; // The list of instruction sets supported by this app. This is currently @@ -14992,7 +14985,7 @@ public class PackageManagerService extends IPackageManager.Stub int installFlags, String installerPackageName, String volumeUuid, UserHandle user, String[] instructionSets, String abiOverride, String[] installGrantPermissions, - String traceMethod, int traceCookie, Certificate[][] certificates, + String traceMethod, int traceCookie, PackageParser.SigningDetails signingDetails, int installReason) { this.origin = origin; this.move = move; @@ -15006,7 +14999,7 @@ public class PackageManagerService extends IPackageManager.Stub this.installGrantPermissions = installGrantPermissions; this.traceMethod = traceMethod; this.traceCookie = traceCookie; - this.certificates = certificates; + this.signingDetails = signingDetails; this.installReason = installReason; } @@ -15102,7 +15095,7 @@ public class PackageManagerService extends IPackageManager.Stub params.installerPackageName, params.volumeUuid, params.getUser(), null /*instructionSets*/, params.packageAbiOverride, params.grantedRuntimePermissions, - params.traceMethod, params.traceCookie, params.certificates, + params.traceMethod, params.traceCookie, params.signingDetails, params.installReason); if (isFwdLocked()) { throw new IllegalArgumentException("Forward locking only supported in ASEC"); @@ -15112,7 +15105,7 @@ public class PackageManagerService extends IPackageManager.Stub /** Existing install */ FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) { super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets, - null, null, null, 0, null /*certificates*/, + null, null, null, 0, PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN); this.codeFile = (codePath != null) ? new File(codePath) : null; this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; @@ -15333,7 +15326,7 @@ public class PackageManagerService extends IPackageManager.Stub params.installerPackageName, params.volumeUuid, params.getUser(), null /* instruction sets */, params.packageAbiOverride, params.grantedRuntimePermissions, - params.traceMethod, params.traceCookie, params.certificates, + params.traceMethod, params.traceCookie, params.signingDetails, params.installReason); } @@ -15672,7 +15665,8 @@ public class PackageManagerService extends IPackageManager.Stub } } else { // default to original signature matching - if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures) + if (compareSignatures(oldPackage.mSigningDetails.signatures, + pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) { res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "New package has a different signature: " + pkgName); @@ -16484,14 +16478,8 @@ public class PackageManagerService extends IPackageManager.Stub try { // either use what we've been given or parse directly from the APK - if (args.certificates != null) { - try { - PackageParser.populateCertificates(pkg, args.certificates); - } catch (PackageParserException e) { - // there was something wrong with the certificates we were given; - // try to pull them from the APK - PackageParser.collectCertificates(pkg, parseFlags); - } + if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) { + pkg.setSigningDetails(args.signingDetails); } else { PackageParser.collectCertificates(pkg, parseFlags); } @@ -16610,7 +16598,8 @@ public class PackageManagerService extends IPackageManager.Stub final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg); final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg); final boolean compatMatch = verifySignatures( - signatureCheckPs, pkg.mSignatures, compareCompat, compareRecover); + signatureCheckPs, pkg.mSigningDetails.signatures, compareCompat, + compareRecover); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { synchronized (mPackages) { @@ -16661,7 +16650,7 @@ public class PackageManagerService extends IPackageManager.Stub sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg); } else { sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures, - pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; + pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH; } if (!sigsOk) { // If the owning package is the system itself, we log but allow @@ -16937,7 +16926,8 @@ public class PackageManagerService extends IPackageManager.Stub for (ActivityIntentInfo filter : a.intents) { if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) { if (DEBUG_DOMAIN_VERIFICATION) { - Slog.d(TAG, "Intent filter needs verification, so processing all filters"); + Slog.d(TAG, + "Intent filter needs verification, so processing all filters"); } needToVerify = true; break; @@ -22245,8 +22235,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); final OriginInfo origin = OriginInfo.fromExistingFile(codeFile); final InstallParams params = new InstallParams(origin, move, installObserver, installFlags, installerPackageName, volumeUuid, null /*verificationInfo*/, user, - packageAbiOverride, null /*grantedPermissions*/, null /*certificates*/, - PackageManager.INSTALL_REASON_UNKNOWN); + packageAbiOverride, null /*grantedPermissions*/, + PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN); params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index fbf3d82455c8..37f9a74fe0ba 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -17,8 +17,6 @@ package com.android.server.pm; import android.content.pm.PackageParser; -import android.content.pm.PackageUserState; -import android.content.pm.SELinuxUtil; import android.content.pm.Signature; import android.os.Environment; import android.util.Slog; @@ -453,7 +451,7 @@ final class Policy { public String getMatchedSeInfo(PackageParser.Package pkg) { // Check for exact signature matches across all certs. Signature[] certs = mCerts.toArray(new Signature[0]); - if (!Signature.areExactMatch(certs, pkg.mSignatures)) { + if (!Signature.areExactMatch(certs, pkg.mSigningDetails.signatures)) { return null; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 4cf18149d853..b5d3af1c6a28 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -955,7 +955,7 @@ public final class Settings { } // Update signatures if needed. if (p.signatures.mSignatures == null) { - p.signatures.assignSignatures(pkg.mSignatures); + p.signatures.assignSignatures(pkg.mSigningDetails.signatures); } // Update flags if needed. if (pkg.applicationInfo.flags != p.pkgFlags) { @@ -964,7 +964,7 @@ public final class Settings { // If this app defines a shared user id initialize // the shared user signatures as well. if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) { - p.sharedUser.signatures.assignSignatures(pkg.mSignatures); + p.sharedUser.signatures.assignSignatures(pkg.mSigningDetails.signatures); } // Update static shared library dependencies if needed if (pkg.usesStaticLibraries != null && pkg.usesStaticLibrariesVersions != null @@ -4565,10 +4565,8 @@ public final class Settings { } pw.print(prefix); pw.print(" versionName="); pw.println(ps.pkg.mVersionName); pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, ps.pkg); pw.println(); - final int apkSigningVersion = PackageParser.getApkSigningVersion(ps.pkg); - if (apkSigningVersion != PackageParser.APK_SIGNING_UNKNOWN) { - pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion); - } + final int apkSigningVersion = ps.pkg.mSigningDetails.signatureSchemeVersion; + pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion); pw.print(prefix); pw.print(" applicationInfo="); pw.println(ps.pkg.applicationInfo.toString()); pw.print(prefix); pw.print(" flags="); printFlags(pw, ps.pkg.applicationInfo.flags, diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 34c3ce359e86..6e07eaac9c44 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -27,7 +27,6 @@ import android.app.admin.DevicePolicyManager; import android.companion.CompanionDeviceManager; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageList; @@ -62,8 +61,6 @@ import android.util.Slog; import android.util.Xml; import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; -import com.android.server.pm.PackageManagerService; -import com.android.server.pm.PackageSetting; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -1169,7 +1166,8 @@ public final class DefaultPermissionGrantPolicy { final String systemPackageName = mServiceInternal.getKnownPackageName( PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM); final PackageParser.Package systemPackage = getPackage(systemPackageName); - return compareSignatures(systemPackage.mSignatures, pkg.mSignatures) + return compareSignatures(systemPackage.mSigningDetails.signatures, + pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH; } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 90ac4ab7dd42..786b998862de 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -29,7 +29,6 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -56,21 +55,17 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; -import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.RoSystemProperties; import com.android.internal.util.ArrayUtils; -import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; -import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; -import com.android.server.pm.ProcessLoggingHandler; import com.android.server.pm.SharedUserSetting; import com.android.server.pm.UserManagerService; import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; @@ -1015,10 +1010,10 @@ Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages)); final PackageParser.Package systemPackage = mPackageManagerInt.getPackage(systemPackageName); boolean allowed = (PackageManagerServiceUtils.compareSignatures( - bp.getSourceSignatures(), pkg.mSignatures) + bp.getSourceSignatures(), pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH) || (PackageManagerServiceUtils.compareSignatures( - systemPackage.mSignatures, pkg.mSignatures) + systemPackage.mSigningDetails.signatures, pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH); if (!allowed && (privilegedPermission || oemPermission)) { if (pkg.isSystem()) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 252eff567cfd..722486351e19 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -49,6 +49,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -3496,37 +3497,47 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override void assignChildLayers(SurfaceControl.Transaction t) { - int layer = 0; - // We allow stacks to change visual order from the AM specified order due to - // Z-boosting during animations. However we must take care to ensure TaskStacks - // which are marked as alwaysOnTop remain that way. - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - s.assignChildLayers(); - if (!s.needsZBoost() && !s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); + final int NORMAL_STACK_STATE = 0; + final int SPLIT_SCREEN_STACK_STATE = 1; + final int ASSISTANT_STACK_STATE = 2; + final int BOOSTED_STATE = 3; + final int ALWAYS_ON_TOP_STATE = 4; + + int layer = 0; + for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) { + for (int i = 0; i < mChildren.size(); i++) { + final TaskStack s = mChildren.get(i); + layer++; + if (state == NORMAL_STACK_STATE && !s.inSplitScreenPrimaryWindowingMode() && + !s.isActivityTypeAssistant() && + !s.needsZBoost() && !s.isAlwaysOnTop()) { + s.assignLayer(t, layer); + } else if (state == SPLIT_SCREEN_STACK_STATE && + s.inSplitScreenPrimaryWindowingMode()) { + s.assignLayer(t, layer); + } else if (state == ASSISTANT_STACK_STATE && + s.isActivityTypeAssistant()) { + s.assignLayer(t, layer); + } else if (state == BOOSTED_STATE && s.needsZBoost()) { + s.assignLayer(t, layer); + } else if (state == ALWAYS_ON_TOP_STATE && + s.isAlwaysOnTop()) { + s.assignLayer(t, layer); + } } - } - for (int i = 0; i < mChildren.size(); i++) { - final TaskStack s = mChildren.get(i); - if (s.needsZBoost() && !s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); + // The appropriate place for App-Transitions to occur is right + // above all other animations but still below things in the Picture-and-Picture + // windowing mode. + if (state == BOOSTED_STATE && mAppAnimationLayer != null) { + t.setLayer(mAppAnimationLayer, layer++); } } for (int i = 0; i < mChildren.size(); i++) { final TaskStack s = mChildren.get(i); - if (s.isAlwaysOnTop()) { - s.assignLayer(t, layer++); - } + s.assignChildLayers(t); } - // The appropriate place for App-Transitions to occur is right - // above all other animations but still below things in the Picture-and-Picture - // windowing mode. - if (mAppAnimationLayer != null) { - t.setLayer(mAppAnimationLayer, layer++); - } } @Override @@ -3560,6 +3571,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo && imeContainer.getSurfaceControl() != null; for (int j = 0; j < mChildren.size(); ++j) { final WindowToken wt = mChildren.get(j); + + // The divider is unique in that it does not have an AppWindowToken but needs to be + // interleaved with them. In particular it must be above any split-screen stacks + // but below any always-on-top stacks. + if (wt.windowType == TYPE_DOCK_DIVIDER) { + final TaskStack dockedStack = getSplitScreenPrimaryStack(); + if (dockedStack != null) { + wt.assignRelativeLayer(t, dockedStack.getSurfaceControl(), + Integer.MAX_VALUE); + continue; + } + } wt.assignLayer(t, j); wt.assignChildLayers(t); diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java index 3a41eb0e2afc..dc62cc89c14d 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java @@ -67,6 +67,9 @@ class SurfaceAnimationRunner { @VisibleForTesting final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>(); + @GuardedBy("mLock") + private boolean mAnimationStartDeferred; + SurfaceAnimationRunner() { this(null /* callbackProvider */, null /* animatorFactory */, new Transaction()); } @@ -86,13 +89,41 @@ class SurfaceAnimationRunner { : SfValueAnimator::new; } + /** + * Defers starting of animations until {@link #continueStartingAnimations} is called. This + * method is NOT nestable. + * + * @see #continueStartingAnimations + */ + void deferStartingAnimations() { + synchronized (mLock) { + mAnimationStartDeferred = true; + } + } + + /** + * Continues starting of animations. + * + * @see #deferStartingAnimations + */ + void continueStartingAnimations() { + synchronized (mLock) { + mAnimationStartDeferred = false; + if (!mPendingAnimations.isEmpty()) { + mChoreographer.postFrameCallback(this::startAnimations); + } + } + } + void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, Runnable finishCallback) { synchronized (mLock) { final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, finishCallback); mPendingAnimations.put(animationLeash, runningAnim); - mChoreographer.postFrameCallback(this::stepAnimation); + if (!mAnimationStartDeferred) { + mChoreographer.postFrameCallback(this::startAnimations); + } // Some animations (e.g. move animations) require the initial transform to be applied // immediately. @@ -185,7 +216,7 @@ class SurfaceAnimationRunner { a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); } - private void stepAnimation(long frameTimeNanos) { + private void startAnimations(long frameTimeNanos) { synchronized (mLock) { startPendingAnimationsLocked(); } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index eb8eae1a95d7..bdda944f236b 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -44,7 +44,6 @@ import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER; import android.annotation.CallSuper; import android.content.res.Configuration; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.RemoteException; @@ -146,7 +145,6 @@ public class TaskStack extends WindowContainer<Task> implements * For {@link #prepareSurfaces}. */ final Rect mTmpDimBoundsRect = new Rect(); - private final Point mLastSurfaceSize = new Point(); TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { super(service); @@ -716,10 +714,11 @@ public class TaskStack extends WindowContainer<Task> implements @Override public void onConfigurationChanged(Configuration newParentConfig) { final int prevWindowingMode = getWindowingMode(); + super.onConfigurationChanged(newParentConfig); + // Only need to update surface size here since the super method will handle updating // surface position. updateSurfaceSize(getPendingTransaction()); - super.onConfigurationChanged(newParentConfig); final int windowingMode = getWindowingMode(); if (mDisplayContent == null || prevWindowingMode == windowingMode) { @@ -745,13 +744,7 @@ public class TaskStack extends WindowContainer<Task> implements } final Rect stackBounds = getBounds(); - final int width = stackBounds.width(); - final int height = stackBounds.height(); - if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { - return; - } - transaction.setSize(mSurfaceControl, width, height); - mLastSurfaceSize.set(width, height); + transaction.setSize(mSurfaceControl, stackBounds.width(), stackBounds.height()); } @Override diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java index 031b57b73b05..0863ee9eebb3 100644 --- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java +++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java @@ -89,7 +89,10 @@ public class WindowAnimationSpec implements AnimationSpec { if (mStackClipMode == STACK_CLIP_NONE) { t.setWindowCrop(leash, tmp.transformation.getClipRect()); } else if (mStackClipMode == STACK_CLIP_AFTER_ANIM) { - t.setFinalCrop(leash, mStackBounds); + mTmpRect.set(mStackBounds); + // Offset stack bounds to stack position so the final crop is in screen space. + mTmpRect.offsetTo(mPosition.x, mPosition.y); + t.setFinalCrop(leash, mTmpRect); t.setWindowCrop(leash, tmp.transformation.getClipRect()); } else { mTmpRect.set(mStackBounds); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index af314101cd2b..5d445eff0c92 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -95,7 +95,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< protected final WindowManagerService mService; private final Point mTmpPos = new Point(); - protected final Point mLastSurfacePosition = new Point(); /** Total number of elements in this subtree, including our own hierarchy element. */ private int mTreeWeight = 1; @@ -1178,12 +1177,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } getRelativePosition(mTmpPos); - if (mTmpPos.equals(mLastSurfacePosition)) { - return; - } - transaction.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y); - mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y); for (int i = mChildren.size() - 1; i >= 0; i--) { mChildren.get(i).updateSurfacePosition(transaction); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 08fb037e6195..b3809dd8f6c9 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1543,10 +1543,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override public boolean canAffectSystemUiFlags() { final boolean translucent = mAttrs.alpha == 0.0f; + if (translucent) { + return false; + } if (mAppToken == null) { final boolean shown = mWinAnimator.getShown(); final boolean exiting = mAnimatingExit || mDestroying; - return shown && !exiting && !translucent; + return shown && !exiting; } else { return !mAppToken.isHidden(); } @@ -4484,7 +4487,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Leash is now responsible for position, so set our position to 0. t.setPosition(mSurfaceControl, 0, 0); - mLastSurfacePosition.set(0, 0); } @Override @@ -4500,9 +4502,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition); - if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) { + if (!mSurfaceAnimator.hasLeash()) { t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); - mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); } } diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index bdab9c765d36..08f49f689323 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -349,21 +349,28 @@ class WindowSurfacePlacer { animLp = null; } - processApplicationsAnimatingInPlace(transit); - - mTmpLayerAndToken.token = null; - handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken); - final AppWindowToken topClosingApp = mTmpLayerAndToken.token; - final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, voiceInteraction); - - mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp); - - final int flags = mService.mAppTransition.getTransitFlags(); - int layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp, - topClosingApp, mService.mOpeningApps, mService.mClosingApps); - handleNonAppWindowsInTransition(transit, flags); - mService.mAppTransition.postAnimationCallback(); - mService.mAppTransition.clear(); + final int layoutRedo; + mService.mSurfaceAnimationRunner.deferStartingAnimations(); + try { + processApplicationsAnimatingInPlace(transit); + + mTmpLayerAndToken.token = null; + handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken); + final AppWindowToken topClosingApp = mTmpLayerAndToken.token; + final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, + voiceInteraction); + + mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp); + + final int flags = mService.mAppTransition.getTransitFlags(); + layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp, + topClosingApp, mService.mOpeningApps, mService.mClosingApps); + handleNonAppWindowsInTransition(transit, flags); + mService.mAppTransition.postAnimationCallback(); + mService.mAppTransition.clear(); + } finally { + mService.mSurfaceAnimationRunner.continueStartingAnimations(); + } mService.mTaskSnapshotController.onTransitionStarting(); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index 6b09363cff0b..bcbf40e191e6 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -47,7 +47,7 @@ import org.junit.Test; * Tests for the {@link ActivityStack} class. * * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.am.ActivityStackTests + * atest ActivityStackTests */ @SmallTest @Presubmit @@ -104,6 +104,29 @@ public class ActivityStackTests extends ActivityTestsBase { } @Test + public void testPrimarySplitScreenToFullscreenWhenMovedToBack() throws Exception { + // Create primary splitscreen stack. This will create secondary stacks and places the + // existing fullscreen stack on the bottom. + final ActivityStack primarySplitScreen = mService.mStackSupervisor.getDefaultDisplay() + .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + + // Assert windowing mode. + assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + + // Move primary to back. + primarySplitScreen.moveToBack("testPrimarySplitScreenToFullscreenWhenMovedToBack", + null /* task */); + + // Assert that stack is at the bottom. + assertEquals(mService.mStackSupervisor.getDefaultDisplay().getIndexOf(primarySplitScreen), + 0); + + // Ensure no longer in splitscreen. + assertEquals(primarySplitScreen.getWindowingMode(), WINDOWING_MODE_FULLSCREEN); + } + + @Test public void testStopActivityWhenActivityDestroyed() throws Exception { final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build(); r.info.flags |= ActivityInfo.FLAG_NO_HISTORY; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 32b0b266bafc..49601c32cdc2 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -34,7 +34,6 @@ import java.io.File; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; -import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -262,14 +261,13 @@ public class PackageParserTest { assertBundleApproximateEquals(a.mAppMetaData, b.mAppMetaData); assertEquals(a.mVersionName, b.mVersionName); assertEquals(a.mSharedUserId, b.mSharedUserId); - assertTrue(Arrays.equals(a.mSignatures, b.mSignatures)); - assertTrue(Arrays.equals(a.mCertificates, b.mCertificates)); + assertTrue(Arrays.equals(a.mSigningDetails.signatures, b.mSigningDetails.signatures)); assertTrue(Arrays.equals(a.mLastPackageUsageTimeInMills, b.mLastPackageUsageTimeInMills)); assertEquals(a.mExtras, b.mExtras); assertEquals(a.mRestrictedAccountType, b.mRestrictedAccountType); assertEquals(a.mRequiredAccountType, b.mRequiredAccountType); assertEquals(a.mOverlayTarget, b.mOverlayTarget); - assertEquals(a.mSigningKeys, b.mSigningKeys); + assertEquals(a.mSigningDetails.publicKeys, b.mSigningDetails.publicKeys); assertEquals(a.mUpgradeKeySets, b.mUpgradeKeySets); assertEquals(a.mKeySetMapping, b.mKeySetMapping); assertEquals(a.cpuAbiOverride, b.cpuAbiOverride); @@ -495,14 +493,16 @@ public class PackageParserTest { pkg.mAppMetaData = new Bundle(); pkg.mVersionName = "foo17"; pkg.mSharedUserId = "foo18"; - pkg.mSignatures = new Signature[] { new Signature(new byte[16]) }; - pkg.mCertificates = new Certificate[][] { new Certificate[] { null }}; + pkg.mSigningDetails = + new PackageParser.SigningDetails( + new Signature[] { new Signature(new byte[16]) }, + 2, + new ArraySet<>()); pkg.mExtras = new Bundle(); pkg.mRestrictedAccountType = "foo19"; pkg.mRequiredAccountType = "foo20"; pkg.mOverlayTarget = "foo21"; pkg.mOverlayPriority = 100; - pkg.mSigningKeys = new ArraySet<>(); pkg.mUpgradeKeySets = new ArraySet<>(); pkg.mKeySetMapping = new ArrayMap<>(); pkg.cpuAbiOverride = "foo22"; diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java index 17fe6427f756..4831fcd67314 100644 --- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java @@ -162,6 +162,20 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase { verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L)); } + @Test + public void testDeferStartingAnimations() throws Exception { + mSurfaceAnimationRunner.deferStartingAnimations(); + mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, + mMockTransaction, this::finishedCallback); + waitUntilNextFrame(); + assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + mSurfaceAnimationRunner.continueStartingAnimations(); + waitUntilNextFrame(); + assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + mFinishCallbackLatch.await(1, SECONDS); + assertFinishCallbackCalled(); + } + private void waitUntilNextFrame() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java index 9cdef16194ff..f8db4faaa81f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; @@ -72,6 +73,19 @@ public class WindowAnimationSpecTest { } @Test + public void testApply_clipAfterOffsetPosition() { + // Stack bounds is (0, 0, 10, 10) position is (20, 40) + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, + new Point(20, 40), mStackBounds, false /* canSkipFirstFrame */, + STACK_CLIP_AFTER_ANIM); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty)); + verify(mTransaction).setFinalCrop(eq(mSurfaceControl), + argThat(rect -> rect.left == 20 && rect.top == 40 && rect.right == 30 + && rect.bottom == 50)); + } + + @Test public void testApply_clipBeforeNoAnimationBounds() { // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0) WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null, diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java index 67db5f4d12dd..7be203a99391 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import android.util.MergedConfiguration; import android.view.WindowManager; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,8 +44,7 @@ import static org.junit.Assert.assertTrue; /** * Tests for the {@link WindowState} class. * - * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.wm.WindowStateTests + * atest FrameworksServicesTests:com.android.server.wm.WindowStateTests */ @SmallTest @Presubmit @@ -213,6 +211,18 @@ public class WindowStateTests extends WindowTestsBase { testPrepareWindowToDisplayDuringRelayout(true /*wasVisible*/); } + @Test + public void testCanAffectSystemUiFlags() throws Exception { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + app.mToken.setHidden(false); + assertTrue(app.canAffectSystemUiFlags()); + app.mToken.setHidden(true); + assertFalse(app.canAffectSystemUiFlags()); + app.mToken.setHidden(false); + app.mAttrs.alpha = 0.0f; + assertFalse(app.canAffectSystemUiFlags()); + } + private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) { final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index 6468763440a5..bcc9a1cbab7d 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -21,10 +21,12 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; @@ -74,11 +76,11 @@ public class ZOrderingTests extends WindowTestsBase { return super.setRelativeLayer(sc, relativeTo, layer); } - int getLayer(SurfaceControl sc) { + private int getLayer(SurfaceControl sc) { return mLayersForControl.getOrDefault(sc, 0); } - SurfaceControl getRelativeLayer(SurfaceControl sc) { + private SurfaceControl getRelativeLayer(SurfaceControl sc) { return mRelativeLayersForControl.get(sc); } }; @@ -146,8 +148,9 @@ public class ZOrderingTests extends WindowTestsBase { return p; } - void assertZOrderGreaterThan(LayerRecordingTransaction t, - SurfaceControl left, SurfaceControl right) throws Exception { + + void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left, + SurfaceControl right) throws Exception { final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left); final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right); @@ -171,9 +174,12 @@ public class ZOrderingTests extends WindowTestsBase { } } - void assertWindowLayerGreaterThan(LayerRecordingTransaction t, - WindowState left, WindowState right) throws Exception { - assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl()); + void assertWindowHigher(WindowState left, WindowState right) throws Exception { + assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl()); + } + + WindowState createWindow(String name) { + return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); } @Test @@ -184,38 +190,37 @@ public class ZOrderingTests extends WindowTestsBase { // The Ime has an higher base layer than app windows and lower base layer than system // windows, so it should be above app windows and below system windows if there isn't an IME // target. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception { - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); sWm.mInputMethodTarget = imeAppTarget; + mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows and below system windows if it is targeting an app // window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception { - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, "imeAppTargetChildAboveWindow"); @@ -228,41 +233,38 @@ public class ZOrderingTests extends WindowTestsBase { // Ime should be above all app windows except for child windows that are z-ordered above it // and below system windows if it is targeting an app window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception { - final WindowState appBelowImeTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget"); - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); - final WindowState appAboveImeTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget"); + final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); + final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); sWm.mInputMethodTarget = imeAppTarget; mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows except for non-fullscreen app window above it and // below system windows if it is targeting an app window. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget); - assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(mImeWindow, appBelowImeTarget); + assertWindowHigher(appAboveImeTarget, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test @@ -276,20 +278,20 @@ public class ZOrderingTests extends WindowTestsBase { // The IME target base layer is higher than all window except for the nav bar window, so the // IME should be above all windows except for the nav bar. - assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); + assertWindowHigher(mImeWindow, imeSystemOverlayTarget); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mImeWindow, mDockedDividerWindow); // The IME has a higher base layer than the status bar so we may expect it to go // above the status bar once they are both in the Non-App layer, as past versions of this // test enforced. However this seems like the wrong behavior unless the status bar is the // IME target. - assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); - assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test @@ -297,17 +299,18 @@ public class ZOrderingTests extends WindowTestsBase { sWm.mInputMethodTarget = mStatusBarWindow; mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mImeWindow, mDockedDividerWindow); + assertWindowHigher(mImeWindow, mStatusBarWindow); // And, IME dialogs should always have an higher layer than the IME. - assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow); + assertWindowHigher(mImeDialogWindow, mImeWindow); } @Test public void testStackLayers() throws Exception { + final WindowState anyWindow1 = createWindow("anyWindow"); final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, "pinnedStackWindow"); @@ -317,12 +320,17 @@ public class ZOrderingTests extends WindowTestsBase { final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, mDisplayContent, "assistantStackWindow"); + final WindowState anyWindow2 = createWindow("anyWindow2"); mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow); - assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow); + // We compare the split-screen windowing mode to two different normal windowing + // mode windows added before and after it to ensure the correct Z ordering irrespective + // of ordering in the child list. + assertWindowHigher(dockedStackWindow, anyWindow1); + assertWindowHigher(dockedStackWindow, anyWindow2); + assertWindowHigher(assistantStackWindow, dockedStackWindow); + assertWindowHigher(pinnedStackWindow, assistantStackWindow); } @Test @@ -337,9 +345,9 @@ public class ZOrderingTests extends WindowTestsBase { // Ime should be above all app windows and below system windows if it is targeting an app // window. - assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow); - assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow); - assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel); + assertWindowHigher(navBarPanel, mNavBarWindow); + assertWindowHigher(statusBarPanel, mStatusBarWindow); + assertWindowHigher(statusBarSubPanel, statusBarPanel); } @Test @@ -347,8 +355,7 @@ public class ZOrderingTests extends WindowTestsBase { // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA // then we can drop all negative layering on the windowing side. - final WindowState anyWindow = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow"); + final WindowState anyWindow = createWindow("anyWindow"); final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent, "TypeApplicationMediaChild"); final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY, @@ -356,7 +363,32 @@ public class ZOrderingTests extends WindowTestsBase { mDisplayContent.assignChildLayers(mTransaction); - assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild); - assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child); + assertWindowHigher(anyWindow, mediaOverlayChild); + assertWindowHigher(mediaOverlayChild, child); + } + + @Test + public void testDockedDividerPosition() throws Exception { + final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, + ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, + "pinnedStackWindow"); + final WindowState splitScreenWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, + mDisplayContent, "splitScreenWindow"); + final WindowState splitScreenSecondaryWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, + TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); + final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, + mDisplayContent, "assistantStackWindow"); + final WindowState dockedDividerWindow = createWindow(null, TYPE_DOCK_DIVIDER, + mDisplayContent, "dockedDivider"); + + mDisplayContent.assignChildLayers(mTransaction); + + assertWindowHigher(dockedDividerWindow, splitScreenWindow); + assertWindowHigher(dockedDividerWindow, splitScreenSecondaryWindow); + assertWindowHigher(assistantStackWindow, dockedDividerWindow); + assertWindowHigher(pinnedStackWindow, dockedDividerWindow); } } diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java index 9b588fa373bf..e0970f2e04fc 100644 --- a/services/usage/java/com/android/server/usage/AppStandbyController.java +++ b/services/usage/java/com/android/server/usage/AppStandbyController.java @@ -36,7 +36,6 @@ import android.app.AppGlobals; import android.app.admin.DevicePolicyManager; import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageEvents; -import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; @@ -82,6 +81,8 @@ import com.android.server.LocalServices; import java.io.File; import java.io.PrintWriter; +import java.time.Duration; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -1278,10 +1279,10 @@ public class AppStandbyController { synchronized (mAppIdleLock) { // Default: 24 hours between paroles - mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL, + mAppIdleParoleIntervalMillis = mParser.getDurationMillis(KEY_PAROLE_INTERVAL, COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE); - mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION, + mAppIdleParoleDurationMillis = mParser.getDurationMillis(KEY_PAROLE_DURATION, COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null); @@ -1308,7 +1309,15 @@ public class AppStandbyController { if (thresholds.length == THRESHOLD_BUCKETS.length) { long[] array = new long[THRESHOLD_BUCKETS.length]; for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) { - array[i] = Long.parseLong(thresholds[i]); + try { + if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) { + array[i] = Duration.parse(thresholds[i]).toMillis(); + } else { + array[i] = Long.parseLong(thresholds[i]); + } + } catch (NumberFormatException|DateTimeParseException e) { + return defaults; + } } return array; } else { diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index d598649926ad..2fec20ab861e 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -168,8 +168,11 @@ public class StorageStatsService extends IStorageStatsManager.Stub { public boolean isReservedSupported(String volumeUuid, String callingPackage) { enforcePermission(Binder.getCallingUid(), callingPackage); - // TODO: implement as part of b/62024591 - return false; + if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) { + return SystemProperties.getBoolean(StorageManager.PROP_HAS_RESERVED, false); + } else { + return false; + } } @Override diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 6a47d05018d6..6b40e7fa4981 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1724,6 +1724,15 @@ public class CarrierConfigManager { */ public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool"; + /** + * Determines whether we should show a warning asking the user to check with their carrier + * on pricing when the user enabled data roaming. + * default to false. + * @hide + */ + public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = + "check_pricing_with_carrier_data_roaming_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -2010,6 +2019,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false); sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false); + sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false); } /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index a49479916f24..2396a9eee92f 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -548,7 +548,8 @@ public class SubscriptionManager { */ @Deprecated public static SubscriptionManager from(Context context) { - return context.getSystemService(SubscriptionManager.class); + return (SubscriptionManager) context + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); } /** diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index 176057ddc23e..9b114a8d2315 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -251,8 +251,8 @@ public class EuiccManager { * * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready, * {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned. + * TODO(b/35851809): Make this a SystemApi. */ - @SystemApi public int getOtaStatus() { if (!isEnabled()) { return EUICC_OTA_STATUS_UNAVAILABLE; diff --git a/test-base/Android.mk b/test-base/Android.mk index 5e5d0401dd3c..861385467a0f 100644 --- a/test-base/Android.mk +++ b/test-base/Android.mk @@ -112,17 +112,6 @@ update-android-test-base-api: $(ANDROID_TEST_BASE_OUTPUT_API_FILE) | $(ACP) endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true -# Build the legacy.test.stubs library -# =================================== -include $(CLEAR_VARS) - -LOCAL_MODULE := legacy.test.stubs -LOCAL_SDK_VERSION := current - -LOCAL_STATIC_JAVA_LIBRARIES := android.test.base.stubs - -include $(BUILD_STATIC_JAVA_LIBRARY) - ifeq ($(HOST_OS),linux) # Build the legacy-performance-test-hostdex library # ================================================= diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk index 088f322ed324..6e0d58a2a881 100644 --- a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk +++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk @@ -40,6 +40,6 @@ LOCAL_SRC_FILES := contrast.cpp \ LOCAL_CFLAGS += -Wall -Wextra -Werror -Wno-unused-parameter -LOCAL_NDK_STL_VARIANT := stlport_static +LOCAL_NDK_STL_VARIANT := c++_static include $(BUILD_SHARED_LIBRARY) diff --git a/tools/aapt2/filter/AbiFilter.h b/tools/aapt2/filter/AbiFilter.h index d875cb2b127b..2832711efb2c 100644 --- a/tools/aapt2/filter/AbiFilter.h +++ b/tools/aapt2/filter/AbiFilter.h @@ -33,6 +33,8 @@ namespace aapt { */ class AbiFilter : public IPathFilter { public: + virtual ~AbiFilter() = default; + /** Factory method to create a filter from a list of configuration::Abi. */ static std::unique_ptr<AbiFilter> FromAbiList(const std::vector<configuration::Abi>& abi_list); diff --git a/tools/aapt2/filter/Filter.h b/tools/aapt2/filter/Filter.h index d737dc92e87b..f932f9ccc82e 100644 --- a/tools/aapt2/filter/Filter.h +++ b/tools/aapt2/filter/Filter.h @@ -27,7 +27,7 @@ namespace aapt { /** A filter to be applied to a path segment. */ class IPathFilter { public: - ~IPathFilter() = default; + virtual ~IPathFilter() = default; /** Returns true if the path should be kept. */ virtual bool Keep(const std::string& path) = 0; diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp index 102bbf9d07d7..e7b269aaa9a5 100644 --- a/tools/incident_section_gen/main.cpp +++ b/tools/incident_section_gen/main.cpp @@ -408,6 +408,9 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { splitAndPrint(s.args()); printf(" NULL),\n"); break; + case SECTION_LOG: + printf(" new LogSection(%d, %s),\n", field->number(), s.args().c_str()); + break; } } printf(" NULL };\n"); diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 89749fb52bb4..bbe6d63073c1 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -166,7 +166,15 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, attributionDecl.fields.front().name.c_str()); fprintf(out, " event.begin();\n"); for (const auto &chainField : attributionDecl.fields) { - fprintf(out, " event << %s[i];\n", chainField.name.c_str()); + if (chainField.javaType == JAVA_TYPE_STRING) { + fprintf(out, " if (%s[i] != NULL) {\n", chainField.name.c_str()); + fprintf(out, " event << %s[i];\n", chainField.name.c_str()); + fprintf(out, " } else {\n"); + fprintf(out, " event << \"\";\n"); + fprintf(out, " }\n"); + } else { + fprintf(out, " event << %s[i];\n", chainField.name.c_str()); + } } fprintf(out, " event.end();\n"); fprintf(out, " }\n"); @@ -589,13 +597,18 @@ write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDe fprintf(out, " jstring jstr = " "(jstring)env->GetObjectArrayElement(%s, i);\n", chainField.name.c_str()); - fprintf(out, " ScopedUtfChars* scoped_%s = " + fprintf(out, " if (jstr == NULL) {\n"); + fprintf(out, " %s_vec.push_back(NULL);\n", + chainField.name.c_str()); + fprintf(out, " } else {\n"); + fprintf(out, " ScopedUtfChars* scoped_%s = " "new ScopedUtfChars(env, jstr);\n", chainField.name.c_str()); - fprintf(out, " %s_vec.push_back(scoped_%s->c_str());\n", + fprintf(out, " %s_vec.push_back(scoped_%s->c_str());\n", chainField.name.c_str(), chainField.name.c_str()); - fprintf(out, " scoped_%s_vec.push_back(scoped_%s);\n", + fprintf(out, " scoped_%s_vec.push_back(scoped_%s);\n", chainField.name.c_str(), chainField.name.c_str()); + fprintf(out, " }\n"); fprintf(out, " }\n"); } fprintf(out, "\n"); @@ -648,7 +661,7 @@ write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDe fprintf(out, " env->ReleaseIntArrayElements(%s, %s_array, 0);\n", chainField.name.c_str(), chainField.name.c_str()); } else if (chainField.javaType == JAVA_TYPE_STRING) { - fprintf(out, " for (size_t i = 0; i < %s_length; ++i) {\n", + fprintf(out, " for (size_t i = 0; i < scoped_%s_vec.size(); ++i) {\n", chainField.name.c_str()); fprintf(out, " delete scoped_%s_vec[i];\n", chainField.name.c_str()); fprintf(out, " }\n"); diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index b6ad92614bfe..eaad137b9b6e 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -21,7 +21,9 @@ import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * Describes information about a detected access point. In addition @@ -227,6 +229,50 @@ public class ScanResult implements Parcelable { public long seen; /** + * On devices with multiple hardware radio chains, this class provides metadata about + * each radio chain that was used to receive this scan result (probe response or beacon). + * {@hide} + */ + public static class RadioChainInfo { + /** Vendor defined id for a radio chain. */ + public int id; + /** Detected signal level in dBm (also known as the RSSI) on this radio chain. */ + public int level; + + @Override + public String toString() { + return "RadioChainInfo: id=" + id + ", level=" + level; + } + + @Override + public boolean equals(Object otherObj) { + if (this == otherObj) { + return true; + } + if (!(otherObj instanceof RadioChainInfo)) { + return false; + } + RadioChainInfo other = (RadioChainInfo) otherObj; + return id == other.id && level == other.level; + } + + @Override + public int hashCode() { + return Objects.hash(id, level); + } + }; + + /** + * Information about the list of the radio chains used to receive this scan result + * (probe response or beacon). + * + * For Example: On devices with 2 hardware radio chains, this list could hold 1 or 2 + * entries based on whether this scan result was received using one or both the chains. + * {@hide} + */ + public RadioChainInfo[] radioChainInfos; + + /** * @hide * Update RSSI of the scan result * @param previousRssi @@ -481,6 +527,7 @@ public class ScanResult implements Parcelable { this.isCarrierAp = false; this.carrierApEapType = UNSPECIFIED; this.carrierName = null; + this.radioChainInfos = null; } /** {@hide} */ @@ -502,6 +549,7 @@ public class ScanResult implements Parcelable { this.isCarrierAp = false; this.carrierApEapType = UNSPECIFIED; this.carrierName = null; + this.radioChainInfos = null; } /** {@hide} */ @@ -530,6 +578,7 @@ public class ScanResult implements Parcelable { this.isCarrierAp = false; this.carrierApEapType = UNSPECIFIED; this.carrierName = null; + this.radioChainInfos = null; } /** {@hide} */ @@ -572,6 +621,7 @@ public class ScanResult implements Parcelable { isCarrierAp = source.isCarrierAp; carrierApEapType = source.carrierApEapType; carrierName = source.carrierName; + radioChainInfos = source.radioChainInfos; } } @@ -615,6 +665,7 @@ public class ScanResult implements Parcelable { sb.append(", Carrier AP: ").append(isCarrierAp ? "yes" : "no"); sb.append(", Carrier AP EAP Type: ").append(carrierApEapType); sb.append(", Carrier name: ").append(carrierName); + sb.append(", Radio Chain Infos: ").append(Arrays.toString(radioChainInfos)); return sb.toString(); } @@ -687,6 +738,16 @@ public class ScanResult implements Parcelable { dest.writeInt(isCarrierAp ? 1 : 0); dest.writeInt(carrierApEapType); dest.writeString(carrierName); + + if (radioChainInfos != null) { + dest.writeInt(radioChainInfos.length); + for (int i = 0; i < radioChainInfos.length; i++) { + dest.writeInt(radioChainInfos[i].id); + dest.writeInt(radioChainInfos[i].level); + } + } else { + dest.writeInt(0); + } } /** Implement the Parcelable interface {@hide} */ @@ -759,6 +820,15 @@ public class ScanResult implements Parcelable { sr.isCarrierAp = in.readInt() != 0; sr.carrierApEapType = in.readInt(); sr.carrierName = in.readString(); + n = in.readInt(); + if (n != 0) { + sr.radioChainInfos = new RadioChainInfo[n]; + for (int i = 0; i < n; i++) { + sr.radioChainInfos[i] = new RadioChainInfo(); + sr.radioChainInfos[i].id = in.readInt(); + sr.radioChainInfos[i].level = in.readInt(); + } + } return sr; } diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java new file mode 100644 index 000000000000..689ebbafe527 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java @@ -0,0 +1,193 @@ +/* + * 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. + */ + +package android.net.wifi; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.validateMockitoUsage; + +import android.os.Parcel; +import android.test.suitebuilder.annotation.SmallTest; +import android.net.wifi.WifiScanner.ScanSettings; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockitoAnnotations; + + +/** + * Unit tests for {@link android.net.wifi.WifiScanner}. + */ +@SmallTest +public class ScanResultTest { + public static final String TEST_SSID = "\"test_ssid\""; + public static final String TEST_BSSID = "04:ac:fe:45:34:10"; + public static final String TEST_CAPS = "CCMP"; + public static final int TEST_LEVEL = -56; + public static final int TEST_FREQUENCY = 2412; + public static final long TEST_TSF = 04660l; + + /** + * Setup before tests. + */ + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + /** + * Clean up after tests. + */ + @After + public void cleanup() { + validateMockitoUsage(); + } + + /** + * Verify parcel read/write for ScanResult. + */ + @Test + public void verifyScanResultParcelWithoutRadioChainInfo() throws Exception { + ScanResult writeScanResult = createScanResult(); + ScanResult readScanResult = parcelReadWrite(writeScanResult); + assertScanResultEquals(writeScanResult, readScanResult); + } + + /** + * Verify parcel read/write for ScanResult. + */ + @Test + public void verifyScanResultParcelWithZeroRadioChainInfo() throws Exception { + ScanResult writeScanResult = createScanResult(); + writeScanResult.radioChainInfos = new ScanResult.RadioChainInfo[0]; + ScanResult readScanResult = parcelReadWrite(writeScanResult); + assertNull(readScanResult.radioChainInfos); + } + + /** + * Verify parcel read/write for ScanResult. + */ + @Test + public void verifyScanResultParcelWithRadioChainInfo() throws Exception { + ScanResult writeScanResult = createScanResult(); + writeScanResult.radioChainInfos = new ScanResult.RadioChainInfo[2]; + writeScanResult.radioChainInfos[0] = new ScanResult.RadioChainInfo(); + writeScanResult.radioChainInfos[0].id = 0; + writeScanResult.radioChainInfos[0].level = -45; + writeScanResult.radioChainInfos[1] = new ScanResult.RadioChainInfo(); + writeScanResult.radioChainInfos[1].id = 1; + writeScanResult.radioChainInfos[1].level = -54; + ScanResult readScanResult = parcelReadWrite(writeScanResult); + assertScanResultEquals(writeScanResult, readScanResult); + } + + /** + * Verify copy constructor for ScanResult. + */ + @Test + public void verifyScanResultCopyWithoutRadioChainInfo() throws Exception { + ScanResult scanResult = createScanResult(); + ScanResult copyScanResult = new ScanResult(scanResult); + assertScanResultEquals(scanResult, copyScanResult); + } + + /** + * Verify copy constructor for ScanResult. + */ + @Test + public void verifyScanResultCopyWithRadioChainInfo() throws Exception { + ScanResult scanResult = createScanResult(); + scanResult.radioChainInfos = new ScanResult.RadioChainInfo[2]; + scanResult.radioChainInfos[0] = new ScanResult.RadioChainInfo(); + scanResult.radioChainInfos[0].id = 0; + scanResult.radioChainInfos[0].level = -45; + scanResult.radioChainInfos[1] = new ScanResult.RadioChainInfo(); + scanResult.radioChainInfos[1].id = 1; + scanResult.radioChainInfos[1].level = -54; + ScanResult copyScanResult = new ScanResult(scanResult); + assertScanResultEquals(scanResult, copyScanResult); + } + + /** + * Verify toString for ScanResult. + */ + @Test + public void verifyScanResultToStringWithoutRadioChainInfo() throws Exception { + ScanResult scanResult = createScanResult(); + assertEquals("SSID: \"test_ssid\", BSSID: 04:ac:fe:45:34:10, capabilities: CCMP, " + + "level: -56, frequency: 2412, timestamp: 2480, distance: 0(cm), distanceSd: 0(cm), " + + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, " + + "80211mcResponder: is not supported, Carrier AP: no, " + + "Carrier AP EAP Type: 0, Carrier name: null, " + + "Radio Chain Infos: null", scanResult.toString()); + } + + /** + * Verify toString for ScanResult. + */ + @Test + public void verifyScanResultToStringWithRadioChainInfo() throws Exception { + ScanResult scanResult = createScanResult(); + scanResult.radioChainInfos = new ScanResult.RadioChainInfo[2]; + scanResult.radioChainInfos[0] = new ScanResult.RadioChainInfo(); + scanResult.radioChainInfos[0].id = 0; + scanResult.radioChainInfos[0].level = -45; + scanResult.radioChainInfos[1] = new ScanResult.RadioChainInfo(); + scanResult.radioChainInfos[1].id = 1; + scanResult.radioChainInfos[1].level = -54; + assertEquals("SSID: \"test_ssid\", BSSID: 04:ac:fe:45:34:10, capabilities: CCMP, " + + "level: -56, frequency: 2412, timestamp: 2480, distance: 0(cm), distanceSd: 0(cm), " + + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, " + + "80211mcResponder: is not supported, Carrier AP: no, " + + "Carrier AP EAP Type: 0, Carrier name: null, " + + "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, " + + "RadioChainInfo: id=1, level=-54]", scanResult.toString()); + } + + /** + * Write the provided {@link ScanResult} to a parcel and deserialize it. + */ + private static ScanResult parcelReadWrite(ScanResult writeResult) throws Exception { + Parcel parcel = Parcel.obtain(); + writeResult.writeToParcel(parcel, 0); + parcel.setDataPosition(0); // Rewind data position back to the beginning for read. + return ScanResult.CREATOR.createFromParcel(parcel); + } + + private static ScanResult createScanResult() { + ScanResult result = new ScanResult(); + result.wifiSsid = WifiSsid.createFromAsciiEncoded(TEST_SSID); + result.BSSID = TEST_BSSID; + result.capabilities = TEST_CAPS; + result.level = TEST_LEVEL; + result.frequency = TEST_FREQUENCY; + result.timestamp = TEST_TSF; + return result; + } + + private static void assertScanResultEquals(ScanResult expected, ScanResult actual) { + assertEquals(expected.SSID, actual.SSID); + assertEquals(expected.BSSID, actual.BSSID); + assertEquals(expected.capabilities, actual.capabilities); + assertEquals(expected.level, actual.level); + assertEquals(expected.frequency, actual.frequency); + assertEquals(expected.timestamp, actual.timestamp); + assertArrayEquals(expected.radioChainInfos, actual.radioChainInfos); + } +} |