summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp10
-rw-r--r--cmds/incident_helper/src/ih_util.cpp13
-rw-r--r--cmds/incident_helper/src/ih_util.h7
-rw-r--r--cmds/incident_helper/src/main.cpp3
-rw-r--r--cmds/incident_helper/src/parsers/EventLogTagsParser.cpp84
-rw-r--r--cmds/incident_helper/src/parsers/EventLogTagsParser.h35
-rw-r--r--cmds/incident_helper/testdata/event-log-tags.txt6
-rw-r--r--cmds/incident_helper/tests/EventLogTagsParser_test.cpp125
-rw-r--r--cmds/incident_helper/tests/ih_util_test.cpp19
-rw-r--r--cmds/incidentd/Android.mk49
-rw-r--r--cmds/incidentd/src/Section.cpp187
-rw-r--r--cmds/incidentd/src/Section.h21
-rw-r--r--cmds/incidentd/src/section_list.h2
-rw-r--r--cmds/incidentd/tests/Section_test.cpp22
-rw-r--r--core/java/android/bluetooth/BluetoothA2dp.java88
-rw-r--r--core/java/android/content/pm/PackageBackwardCompatibility.java12
-rw-r--r--core/java/android/content/pm/PackageParser.java257
-rw-r--r--core/java/android/net/IpSecConfig.java6
-rw-r--r--core/java/android/net/IpSecManager.java8
-rw-r--r--core/java/android/net/IpSecTransform.java17
-rw-r--r--core/java/android/os/HidlSupport.java19
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java63
-rw-r--r--core/java/android/widget/TextView.java12
-rw-r--r--core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp128
-rw-r--r--core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h13
-rw-r--r--core/jni/android/graphics/ImageDecoder.cpp136
-rw-r--r--core/proto/android/os/incident.proto51
-rw-r--r--core/proto/android/util/event_log_tags.proto58
-rw-r--r--core/proto/android/util/log.proto88
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/values/config.xml2
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java14
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java334
-rw-r--r--libs/incident/proto/android/section.proto3
-rw-r--r--location/java/android/location/ILocationManager.aidl1
-rw-r--r--location/java/android/location/LocationManager.java29
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java1
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java25
-rw-r--r--services/core/java/com/android/server/EventLogTags.logtags4
-rw-r--r--services/core/java/com/android/server/IpSecService.java16
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java104
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java2
-rw-r--r--services/core/java/com/android/server/VibratorService.java71
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java14
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java53
-rw-r--r--services/core/java/com/android/server/job/JobServiceContext.java15
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java18
-rw-r--r--services/core/java/com/android/server/pm/KeySetManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java32
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java90
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java4
-rw-r--r--services/core/java/com/android/server/pm/Settings.java10
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java6
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java69
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimationRunner.java35
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java9
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java162
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java10
-rw-r--r--test-base/Android.mk11
-rw-r--r--tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.mk2
-rw-r--r--tools/aapt2/filter/AbiFilter.h2
-rw-r--r--tools/aapt2/filter/Filter.h2
-rw-r--r--tools/incident_section_gen/main.cpp3
-rw-r--r--wifi/java/android/net/wifi/ScanResult.java70
-rw-r--r--wifi/tests/src/android/net/wifi/ScanResultTest.java193
80 files changed, 2468 insertions, 712 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..1cf02d35636c 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,18 +62,34 @@ 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)
+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_PROTO)
+GEN_PROTO:=
gen_src_dir:=
-GEN:=
LOCAL_INIT_RC := incidentd.rc
@@ -120,4 +139,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/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/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/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/widget/TextView.java b/core/java/android/widget/TextView.java
index 73d53d4dfc8f..dc54127d85b2 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.
@@ -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);
@@ -10296,7 +10295,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (forAutofill) {
structure.setDataIsSensitive(!mSetFromXmlOrResourceId);
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/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/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/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..c40f3748008b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -173,6 +173,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/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/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/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/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/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/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/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index bcb57efffc19..d895636895d3 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.
*/
@@ -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/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/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/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/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/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);
+ }
+}