Merge "VelocityTracker API Documentation Improvements"
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 4f8faca..7a08cbd 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -222,6 +222,7 @@
},
data: [
"tests/data/**/*.apk",
+ "tests/data/**/*.png",
],
compile_multilib: "first",
test_options: {
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 4431164..10947dc 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -39,6 +39,7 @@
#include "idmap2/PrettyPrintVisitor.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
+#include <fcntl.h>
using android::base::StringPrintf;
using android::binder::Status;
@@ -238,6 +239,9 @@
if (res.dataType == Res_value::TYPE_STRING) {
builder.SetResourceValue(res.resourceName, res.dataType, res.stringData.value(),
res.configuration.value_or(std::string()));
+ } else if (res.binaryData.has_value()) {
+ builder.SetResourceValue(res.resourceName, res.binaryData->get(),
+ res.configuration.value_or(std::string()));
} else {
builder.SetResourceValue(res.resourceName, res.dataType, res.data,
res.configuration.value_or(std::string()));
@@ -264,6 +268,7 @@
file_name.c_str(), kMaxFileNameLength));
}
} while (std::filesystem::exists(path));
+ builder.setFrroPath(path);
const uid_t uid = IPCThreadState::self()->getCallingUid();
if (!UidHasWriteAccessToPath(uid, path)) {
diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
index c773e11..3ad6d58 100644
--- a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
+++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl
@@ -24,5 +24,6 @@
int dataType;
int data;
@nullable @utf8InCpp String stringData;
+ @nullable ParcelFileDescriptor binaryData;
@nullable @utf8InCpp String configuration;
}
\ No newline at end of file
diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h
index 05b0618..9f57710 100644
--- a/cmds/idmap2/include/idmap2/FabricatedOverlay.h
+++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h
@@ -28,6 +28,7 @@
#include "idmap2/ResourceContainer.h"
#include "idmap2/Result.h"
+#include <binder/ParcelFileDescriptor.h>
namespace android::idmap2 {
@@ -45,6 +46,15 @@
const std::string& data_string_value,
const std::string& configuration);
+ Builder& SetResourceValue(const std::string& resource_name,
+ std::optional<android::base::borrowed_fd>&& binary_value,
+ const std::string& configuration);
+
+ inline Builder& setFrroPath(std::string frro_path) {
+ frro_path_ = std::move(frro_path);
+ return *this;
+ }
+
WARN_UNUSED Result<FabricatedOverlay> Build();
private:
@@ -53,6 +63,7 @@
DataType data_type;
DataValue data_value;
std::string data_string_value;
+ std::optional<android::base::borrowed_fd> data_binary_value;
std::string configuration;
};
@@ -60,6 +71,7 @@
std::string name_;
std::string target_package_name_;
std::string target_overlayable_;
+ std::string frro_path_;
std::vector<Entry> entries_;
};
@@ -79,10 +91,14 @@
explicit FabricatedOverlay(pb::FabricatedOverlay&& overlay,
std::string&& string_pool_data_,
+ std::vector<android::base::borrowed_fd> binary_files_,
+ off_t total_binary_bytes_,
std::optional<uint32_t> crc_from_disk = {});
pb::FabricatedOverlay overlay_pb_;
std::string string_pool_data_;
+ std::vector<android::base::borrowed_fd> binary_files_;
+ uint32_t total_binary_bytes_;
std::optional<uint32_t> crc_from_disk_;
mutable std::optional<SerializedData> data_;
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index af4dd89..2214a83 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -19,6 +19,7 @@
#include <optional>
#include <string>
+#include <android-base/unique_fd.h>
#include "androidfw/AssetManager2.h"
#include "idmap2/Result.h"
@@ -41,6 +42,7 @@
DataType data_type;
DataValue data_value;
std::string data_string_value;
+ std::optional<android::base::borrowed_fd> data_binary_value;
};
struct TargetValueWithConfig {
diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
index bde9b0b..d517e29 100644
--- a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
+++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp
@@ -16,6 +16,10 @@
#include "idmap2/FabricatedOverlay.h"
+#include <sys/stat.h> // umask
+#include <sys/types.h> // umask
+
+#include <android-base/file.h>
#include <androidfw/ResourceUtils.h>
#include <androidfw/StringPool.h>
#include <google/protobuf/io/coded_stream.h>
@@ -51,9 +55,13 @@
FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay,
std::string&& string_pool_data,
+ std::vector<android::base::borrowed_fd> binary_files,
+ off_t total_binary_bytes,
std::optional<uint32_t> crc_from_disk)
: overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)),
string_pool_data_(std::move(string_pool_data)),
+ binary_files_(std::move(binary_files)),
+ total_binary_bytes_(total_binary_bytes),
crc_from_disk_(crc_from_disk) {
}
@@ -72,14 +80,23 @@
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, uint8_t data_type, uint32_t data_value,
const std::string& configuration) {
- entries_.emplace_back(Entry{resource_name, data_type, data_value, "", configuration});
+ entries_.emplace_back(
+ Entry{resource_name, data_type, data_value, "", std::nullopt, configuration});
return *this;
}
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, uint8_t data_type, const std::string& data_string_value,
const std::string& configuration) {
- entries_.emplace_back(Entry{resource_name, data_type, 0, data_string_value, configuration});
+ entries_.emplace_back(
+ Entry{resource_name, data_type, 0, data_string_value, std::nullopt, configuration});
+ return *this;
+}
+
+FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
+ const std::string& resource_name, std::optional<android::base::borrowed_fd>&& binary_value,
+ const std::string& configuration) {
+ entries_.emplace_back(Entry{resource_name, 0, 0, "", binary_value, configuration});
return *this;
}
@@ -135,7 +152,7 @@
}
value->second = TargetValue{res_entry.data_type, res_entry.data_value,
- res_entry.data_string_value};
+ res_entry.data_string_value, res_entry.data_binary_value};
}
pb::FabricatedOverlay overlay_pb;
@@ -144,6 +161,11 @@
overlay_pb.set_target_package_name(target_package_name_);
overlay_pb.set_target_overlayable(target_overlayable_);
+ std::vector<android::base::borrowed_fd> binary_files;
+ size_t total_binary_bytes = 0;
+ // 16 for the number of bytes in the frro file before the binary data
+ const size_t FRRO_HEADER_SIZE = 16;
+
for (auto& package : package_map) {
auto package_pb = overlay_pb.add_packages();
package_pb->set_name(package.first);
@@ -162,6 +184,20 @@
if (value.second.data_type == Res_value::TYPE_STRING) {
auto ref = string_pool.MakeRef(value.second.data_string_value);
pb_value->set_data_value(ref.index());
+ } else if (value.second.data_binary_value.has_value()) {
+ pb_value->set_data_type(Res_value::TYPE_STRING);
+ struct stat s;
+ if (fstat(value.second.data_binary_value->get(), &s) == -1) {
+ return Error("unable to get size of binary file: %d", errno);
+ }
+ std::string uri
+ = StringPrintf("frro:/%s?offset=%d&size=%d", frro_path_.c_str(),
+ static_cast<int> (FRRO_HEADER_SIZE + total_binary_bytes),
+ static_cast<int> (s.st_size));
+ total_binary_bytes += s.st_size;
+ binary_files.emplace_back(value.second.data_binary_value->get());
+ auto ref = string_pool.MakeRef(std::move(uri));
+ pb_value->set_data_value(ref.index());
} else {
pb_value->set_data_value(value.second.data_value);
}
@@ -169,10 +205,10 @@
}
}
}
-
android::BigBuffer string_buffer(kBufferSize);
android::StringPool::FlattenUtf8(&string_buffer, string_pool, nullptr);
- return FabricatedOverlay(std::move(overlay_pb), string_buffer.to_string());
+ return FabricatedOverlay(std::move(overlay_pb), string_buffer.to_string(),
+ std::move(binary_files), total_binary_bytes);
}
Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) {
@@ -190,7 +226,7 @@
return Error("Failed to read fabricated overlay version.");
}
- if (version != 1 && version != 2) {
+ if (version < 1 || version > 3) {
return Error("Invalid fabricated overlay version '%u'.", version);
}
@@ -201,7 +237,14 @@
pb::FabricatedOverlay overlay{};
std::string sp_data;
- if (version == 2) {
+ uint32_t total_binary_bytes;
+ if (version == 3) {
+ if (!Read32(stream, &total_binary_bytes)) {
+ return Error("Failed read total binary bytes.");
+ }
+ stream.seekg(total_binary_bytes, std::istream::cur);
+ }
+ if (version >= 2) {
uint32_t sp_size;
if (!Read32(stream, &sp_size)) {
return Error("Failed read string pool size.");
@@ -211,20 +254,15 @@
return Error("Failed to read string pool.");
}
sp_data = buf;
-
- if (!overlay.ParseFromIstream(&stream)) {
- return Error("Failed read fabricated overlay proto.");
- }
- } else {
- if (!overlay.ParseFromIstream(&stream)) {
- return Error("Failed read fabricated overlay proto.");
- }
+ }
+ if (!overlay.ParseFromIstream(&stream)) {
+ return Error("Failed read fabricated overlay proto.");
}
// If the proto version is the latest version, then the contents of the proto must be the same
// when the proto is re-serialized; otherwise, the crc must be calculated because migrating the
// proto to the latest version will likely change the contents of the fabricated overlay.
- return FabricatedOverlay(std::move(overlay), std::move(sp_data),
+ return FabricatedOverlay(std::move(overlay), std::move(sp_data), {}, total_binary_bytes,
version == kFabricatedOverlayCurrentVersion
? std::optional<uint32_t>(crc)
: std::nullopt);
@@ -274,6 +312,14 @@
Write32(stream, kFabricatedOverlayMagic);
Write32(stream, kFabricatedOverlayCurrentVersion);
Write32(stream, (*data)->pb_crc);
+ Write32(stream, total_binary_bytes_);
+ std::string file_contents;
+ for (const android::base::borrowed_fd fd : binary_files_) {
+ if (!ReadFdToString(fd, &file_contents)) {
+ return Error("Failed to read binary file data.");
+ }
+ stream.write(file_contents.data(), file_contents.length());
+ }
Write32(stream, (*data)->sp_data.length());
stream.write((*data)->sp_data.data(), (*data)->sp_data.length());
if (stream.bad()) {
diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
index e804c87..e13a0eb 100644
--- a/cmds/idmap2/tests/FabricatedOverlayTests.cpp
+++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp
@@ -17,6 +17,7 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
#include <idmap2/FabricatedOverlay.h>
+#include "TestHelpers.h"
#include <fstream>
#include <utility>
@@ -41,6 +42,10 @@
}
TEST(FabricatedOverlayTests, SetResourceValue) {
+ auto path = GetTestDataPath() + "/overlay/res/drawable/android.png";
+ auto fd = android::base::unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0) << "errno " << errno << " for path " << path;
+
auto overlay =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
.SetResourceValue(
@@ -54,6 +59,8 @@
Res_value::TYPE_STRING,
"foobar",
"en-rUS-normal-xxhdpi-v21")
+ .SetResourceValue("com.example.target:drawable/dr1", fd, "port-xxhdpi-v7")
+ .setFrroPath("/foo/bar/biz.frro")
.Build();
ASSERT_TRUE(overlay);
auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
@@ -67,19 +74,28 @@
auto pairs = container->GetOverlayData(*info);
ASSERT_TRUE(pairs);
- ASSERT_EQ(4U, pairs->pairs.size());
+ ASSERT_EQ(5U, pairs->pairs.size());
auto string_pool = ResStringPool(pairs->string_pool_data->data.get(),
pairs->string_pool_data->data_length, false);
auto& it = pairs->pairs[0];
- ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
+ ASSERT_EQ("com.example.target:drawable/dr1", it.resource_name);
auto entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
+ ASSERT_EQ(std::string("frro://foo/bar/biz.frro?offset=16&size=8341"),
+ string_pool.string8At(entry->value.data_value).value_or(""));
+ ASSERT_EQ(Res_value::TYPE_STRING, entry->value.data_type);
+ ASSERT_EQ("port-xxhdpi-v7", entry->config);
+
+ it = pairs->pairs[1];
+ ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
+ entry = std::get_if<TargetValueWithConfig>(&it.value);
+ ASSERT_NE(nullptr, entry);
ASSERT_EQ(1U, entry->value.data_value);
ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->value.data_type);
ASSERT_EQ("port", entry->config);
- it = pairs->pairs[1];
+ it = pairs->pairs[2];
ASSERT_EQ("com.example.target:string/int3", it.resource_name);
entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
@@ -87,7 +103,7 @@
ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->value.data_type);
ASSERT_EQ("xxhdpi-v7", entry->config);
- it = pairs->pairs[2];
+ it = pairs->pairs[3];
ASSERT_EQ("com.example.target:string/string1", it.resource_name);
entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
@@ -95,7 +111,7 @@
ASSERT_EQ(std::string("foobar"), string_pool.string8At(entry->value.data_value).value_or(""));
ASSERT_EQ("en-rUS-normal-xxhdpi-v21", entry->config);
- it = pairs->pairs[3];
+ it = pairs->pairs[4];
ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name);
entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 7b7dc17..b473f26 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -260,11 +260,17 @@
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
+ auto path = GetTestDataPath() + "/overlay/res/drawable/android.png";
+ auto fd = android::base::unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0) << "errno " << errno << " for path " << path;
+
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "land-xxhdpi-v7")
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "land")
.SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "xxhdpi-v7")
+ .SetResourceValue("drawable/dr1", fd, "port-xxhdpi-v7")
+ .setFrroPath("/foo/bar/biz.frro")
.Build();
ASSERT_TRUE(frro);
@@ -293,14 +299,19 @@
auto string_pool_data = data->GetStringPoolData();
auto string_pool = ResStringPool(string_pool_data.data(), string_pool_data.size(), false);
+ std::u16string expected_uri = u"frro://foo/bar/biz.frro?offset=16&size=8341";
+ uint32_t uri_index
+ = string_pool.indexOfString(expected_uri.data(), expected_uri.length()).value_or(-1);
const auto& target_inline_entries = data->GetTargetInlineEntries();
- ASSERT_EQ(target_inline_entries.size(), 3U);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, "land-xxhdpi-v7",
+ ASSERT_EQ(target_inline_entries.size(), 4U);
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::drawable::dr1, "port-xxhdpi-v7",
+ Res_value::TYPE_STRING, uri_index);
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::integer::int1, "land-xxhdpi-v7",
Res_value::TYPE_INT_DEC, 2U);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, "land",
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str1, "land",
Res_value::TYPE_REFERENCE, 0x7f010000);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2, "xxhdpi-v7",
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[3], R::target::string::str2, "xxhdpi-v7",
Res_value::TYPE_STRING,
(uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1));
}
diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h
index ad998b9..80c062d 100644
--- a/cmds/idmap2/tests/R.h
+++ b/cmds/idmap2/tests/R.h
@@ -26,24 +26,27 @@
// clang-format off
namespace R::target {
namespace integer { // NOLINT(runtime/indentation_namespace)
- constexpr ResourceId int1 = 0x7f010000;
+ constexpr ResourceId int1 = 0x7f020000;
+ }
+ namespace drawable {
+ constexpr ResourceId dr1 = 0x7f010000;
}
namespace string { // NOLINT(runtime/indentation_namespace)
- constexpr ResourceId not_overlayable = 0x7f020003;
- constexpr ResourceId other = 0x7f020004;
- constexpr ResourceId policy_actor = 0x7f020005;
- constexpr ResourceId policy_config_signature = 0x7f020006;
- constexpr ResourceId policy_odm = 0x7f020007;
- constexpr ResourceId policy_oem = 0x7f020008;
- constexpr ResourceId policy_product = 0x7f020009;
- constexpr ResourceId policy_public = 0x7f02000a;
- constexpr ResourceId policy_signature = 0x7f02000b;
- constexpr ResourceId policy_system = 0x7f02000c;
- constexpr ResourceId policy_system_vendor = 0x7f02000d;
- constexpr ResourceId str1 = 0x7f02000e;
- constexpr ResourceId str2 = 0x7f02000f;
- constexpr ResourceId str3 = 0x7f020010;
- constexpr ResourceId str4 = 0x7f020011;
+ constexpr ResourceId not_overlayable = 0x7f030003;
+ constexpr ResourceId other = 0x7f030004;
+ constexpr ResourceId policy_actor = 0x7f030005;
+ constexpr ResourceId policy_config_signature = 0x7f030006;
+ constexpr ResourceId policy_odm = 0x7f030007;
+ constexpr ResourceId policy_oem = 0x7f030008;
+ constexpr ResourceId policy_product = 0x7f030009;
+ constexpr ResourceId policy_public = 0x7f03000a;
+ constexpr ResourceId policy_signature = 0x7f03000b;
+ constexpr ResourceId policy_system = 0x7f03000c;
+ constexpr ResourceId policy_system_vendor = 0x7f03000d;
+ constexpr ResourceId str1 = 0x7f03000e;
+ constexpr ResourceId str2 = 0x7f03000f;
+ constexpr ResourceId str3 = 0x7f030010;
+ constexpr ResourceId str4 = 0x7f030011;
} // namespace string
} // namespace R::target
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 7112eeb..68164e2 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -79,22 +79,22 @@
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 config count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "0000000a string pool index offset", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id: integer/int1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f02000e target id: string/str1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f03000e target id: string/str1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000b overlay id: string/str1", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f020010 target id: string/str3", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f030010 target id: string/str3", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000c overlay id: string/str3", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f020011 target id: string/str4", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f030011 target id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000d overlay id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id: integer/int1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000b overlay id: string/str1", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f02000e target id: string/str1", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f03000e target id: string/str1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000c overlay id: string/str3", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f020010 target id: string/str3", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f030010 target id: string/str3", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000d overlay id: string/str4", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f020011 target id: string/str4", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f030011 target id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool size", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "........ string pool", stream.str());
}
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 016d427..380e462 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -23,6 +23,7 @@
#include <memory>
#include <string>
+#include <fcntl.h>
#include "R.h"
#include "TestConstants.h"
#include "TestHelpers.h"
@@ -76,7 +77,12 @@
auto target_map = mapping.GetTargetToOverlayMap();
auto entry_map = target_map.find(target_resource);
if (entry_map == target_map.end()) {
- return Error("Failed to find mapping for target resource");
+ std::string keys;
+ for (const auto &pair : target_map) {
+ keys.append(fmt::format("0x{:x}", pair.first)).append(" ");
+ }
+ return Error(R"(Failed to find mapping for target resource "0x%02x": "%s")",
+ target_resource, keys.c_str());
}
auto actual_overlay_resource = std::get_if<ResourceId>(&entry_map->second);
@@ -108,7 +114,12 @@
auto target_map = mapping.GetTargetToOverlayMap();
auto entry_map = target_map.find(target_resource);
if (entry_map == target_map.end()) {
- return Error("Failed to find mapping for target resource");
+ std::string keys;
+ for (const auto &pair : target_map) {
+ keys.append(fmt::format("{:x}", pair.first)).append(" ");
+ }
+ return Error(R"(Failed to find mapping for target resource "0x%02x": "%s")",
+ target_resource, keys.c_str());
}
auto config_map = std::get_if<ConfigMap>(&entry_map->second);
@@ -193,11 +204,16 @@
}
TEST(ResourceMappingTests, FabricatedOverlay) {
+ auto path = GetTestDataPath() + "/overlay/res/drawable/android.png";
+ auto fd = android::base::unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ ASSERT_TRUE(fd > 0) << "errno " << errno << " for path " << path;
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "")
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "")
.SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "")
+ .SetResourceValue("drawable/dr1", fd, "")
+ .setFrroPath("/foo/bar/biz.frro")
.Build();
ASSERT_TRUE(frro);
@@ -214,11 +230,16 @@
auto string_pool_data = res.GetStringPoolData();
auto string_pool = ResStringPool(string_pool_data.data(), string_pool_data.size(), false);
- ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ std::u16string expected_uri = u"frro://foo/bar/biz.frro?offset=16&size=8341";
+ uint32_t uri_index
+ = string_pool.indexOfString(expected_uri.data(), expected_uri.length()).value_or(-1);
+
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x7f010000));
ASSERT_RESULT(MappingExists(res, R::target::string::str2, Res_value::TYPE_STRING,
(uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1)));
+ ASSERT_RESULT(MappingExists(res, R::target::drawable::dr1, Res_value::TYPE_STRING, uri_index));
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U));
}
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
index d5799ad..794d622 100644
--- a/cmds/idmap2/tests/TestConstants.h
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -19,8 +19,8 @@
namespace android::idmap2::TestConstants {
-constexpr const auto TARGET_CRC = 0x7c2d4719;
-constexpr const auto TARGET_CRC_STRING = "7c2d4719";
+constexpr const auto TARGET_CRC = 0xa960a69;
+constexpr const auto TARGET_CRC_STRING = "0a960a69";
constexpr const auto OVERLAY_CRC = 0xb71095cf;
constexpr const auto OVERLAY_CRC_STRING = "b71095cf";
diff --git a/cmds/idmap2/tests/data/overlay/res/drawable/android.png b/cmds/idmap2/tests/data/overlay/res/drawable/android.png
new file mode 100644
index 0000000..b7317b0
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/drawable/android.png
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/build b/cmds/idmap2/tests/data/target/build
index e6df742..cd13a7e 100755
--- a/cmds/idmap2/tests/data/target/build
+++ b/cmds/idmap2/tests/data/target/build
@@ -17,5 +17,7 @@
rm compiled.flata
aapt2 compile res/values/values.xml -o .
-aapt2 link --manifest AndroidManifest.xml -A assets -o target-no-overlayable.apk values_values.arsc.flat
-rm values_values.arsc.flat
\ No newline at end of file
+aapt2 compile res/drawable/dr1.png -o .
+aapt2 link --manifest AndroidManifest.xml -A assets -o target-no-overlayable.apk values_values.arsc.flat drawable_dr1.png.flat
+rm values_values.arsc.flat
+rm drawable_dr1.png.flat
diff --git a/cmds/idmap2/tests/data/target/res/drawable/dr1.png b/cmds/idmap2/tests/data/target/res/drawable/dr1.png
new file mode 100644
index 0000000..1a56e68
--- /dev/null
+++ b/cmds/idmap2/tests/data/target/res/drawable/dr1.png
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
index 57e6c43..aac9081 100644
--- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -63,6 +63,7 @@
<item type="string" name="y" />
<item type="string" name="z" />
<item type="integer" name="int1" />
+ <item type="drawable" name="dr1" />
</policy>
</overlayable>
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index cc3491d..680eeb6 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 4a58c5e..145e737 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index ce18745..1183547 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -349,6 +349,7 @@
}
public final class ServiceManager {
+ method @NonNull public static String[] getDeclaredInstances(@NonNull String);
method public static boolean isDeclared(@NonNull String);
method @Nullable public static android.os.IBinder waitForDeclaredService(@NonNull String);
method @Nullable public static android.os.IBinder waitForService(@NonNull String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 535f6ce..d1c1a17 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1901,6 +1901,7 @@
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
method public static boolean isSplitSystemUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isUserTypeEnabled(@NonNull String);
method public boolean isUsersOnSecondaryDisplaysSupported();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
}
@@ -2627,13 +2628,23 @@
method public int getCarrierIdListVersion();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCertsFromCarrierPrivilegeAccessRules();
method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
+ method @NonNull public android.util.Pair<java.lang.Integer,java.lang.Integer> getHalVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
- method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
+ method @Deprecated public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
method @RequiresPermission(android.Manifest.permission.BIND_TELECOM_CONNECTION_SERVICE) public void setVoiceServiceStateOverride(boolean);
+ field public static final int HAL_SERVICE_DATA = 1; // 0x1
+ field public static final int HAL_SERVICE_IMS = 7; // 0x7
+ field public static final int HAL_SERVICE_MESSAGING = 2; // 0x2
+ field public static final int HAL_SERVICE_MODEM = 3; // 0x3
+ field public static final int HAL_SERVICE_NETWORK = 4; // 0x4
+ field public static final int HAL_SERVICE_SIM = 5; // 0x5
+ field public static final int HAL_SERVICE_VOICE = 6; // 0x6
+ field public static final android.util.Pair HAL_VERSION_UNKNOWN;
+ field public static final android.util.Pair HAL_VERSION_UNSUPPORTED;
field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index f9b8a30..8e21d8c 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -567,6 +567,10 @@
Missing nullability on parameter `destAddress` in method `checkSmsShortCodeDestination`
MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #1:
Missing nullability on parameter `countryIso` in method `checkSmsShortCodeDestination`
+MissingNullability: android.telephony.TelephonyManager#HAL_VERSION_UNKNOWN:
+ Missing nullability on field `HAL_VERSION_UNKNOWN` in class `class android.telephony.TelephonyManager`
+MissingNullability: android.telephony.TelephonyManager#HAL_VERSION_UNSUPPORTED:
+ Missing nullability on field `HAL_VERSION_UNSUPPORTED` in class `class android.telephony.TelephonyManager`
MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag():
Missing nullability on method `getLine1AlphaTag` return
MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion():
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index b3435b1..8b6c4dd 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -28,6 +28,7 @@
import android.os.PatternMatcher;
import android.text.TextUtils;
import android.util.AndroidException;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
@@ -302,7 +303,7 @@
@UnsupportedAppUsage
private int mOrder;
@UnsupportedAppUsage
- private final ArrayList<String> mActions;
+ private final ArraySet<String> mActions;
private ArrayList<String> mCategories = null;
private ArrayList<String> mDataSchemes = null;
private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
@@ -433,7 +434,7 @@
*/
public IntentFilter() {
mPriority = 0;
- mActions = new ArrayList<String>();
+ mActions = new ArraySet<>();
}
/**
@@ -445,7 +446,7 @@
*/
public IntentFilter(String action) {
mPriority = 0;
- mActions = new ArrayList<String>();
+ mActions = new ArraySet<>();
addAction(action);
}
@@ -468,7 +469,7 @@
public IntentFilter(String action, String dataType)
throws MalformedMimeTypeException {
mPriority = 0;
- mActions = new ArrayList<String>();
+ mActions = new ArraySet<>();
addAction(action);
addDataType(dataType);
}
@@ -481,7 +482,7 @@
public IntentFilter(IntentFilter o) {
mPriority = o.mPriority;
mOrder = o.mOrder;
- mActions = new ArrayList<String>(o.mActions);
+ mActions = new ArraySet<>(o.mActions);
if (o.mCategories != null) {
mCategories = new ArrayList<String>(o.mCategories);
}
@@ -742,9 +743,7 @@
* @param action Name of the action to match, such as Intent.ACTION_VIEW.
*/
public final void addAction(String action) {
- if (!mActions.contains(action)) {
- mActions.add(action.intern());
- }
+ mActions.add(action.intern());
}
/**
@@ -758,7 +757,7 @@
* Return an action in the filter.
*/
public final String getAction(int index) {
- return mActions.get(index);
+ return mActions.valueAt(index);
}
/**
@@ -797,8 +796,11 @@
if (ignoreActions == null) {
return !mActions.isEmpty();
}
+ if (mActions.size() > ignoreActions.size()) {
+ return true; // some actions are definitely not ignored
+ }
for (int i = mActions.size() - 1; i >= 0; i--) {
- if (!ignoreActions.contains(mActions.get(i))) {
+ if (!ignoreActions.contains(mActions.valueAt(i))) {
return true;
}
}
@@ -1918,7 +1920,7 @@
int N = countActions();
for (int i=0; i<N; i++) {
serializer.startTag(null, ACTION_STR);
- serializer.attribute(null, NAME_STR, mActions.get(i));
+ serializer.attribute(null, NAME_STR, mActions.valueAt(i));
serializer.endTag(null, ACTION_STR);
}
N = countCategories();
@@ -2313,7 +2315,7 @@
}
public final void writeToParcel(Parcel dest, int flags) {
- dest.writeStringList(mActions);
+ dest.writeStringArray(mActions.toArray(new String[mActions.size()]));
if (mCategories != null) {
dest.writeInt(1);
dest.writeStringList(mCategories);
@@ -2407,8 +2409,9 @@
/** @hide */
public IntentFilter(Parcel source) {
- mActions = new ArrayList<String>();
- source.readStringList(mActions);
+ List<String> actions = new ArrayList<>();
+ source.readStringList(actions);
+ mActions = new ArraySet<>(actions);
if (source.readInt() != 0) {
mCategories = new ArrayList<String>();
source.readStringList(mCategories);
diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java
index 3ca0560..dbefa65 100644
--- a/core/java/android/content/om/FabricatedOverlay.java
+++ b/core/java/android/content/om/FabricatedOverlay.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.os.FabricatedOverlayInternal;
import android.os.FabricatedOverlayInternalEntry;
+import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -169,6 +170,24 @@
return this;
}
+ /**
+ * Sets the value of the fabricated overlay
+ *
+ * @param resourceName name of the target resource to overlay (in the form
+ * [package]:type/entry)
+ * @param value the file descriptor whose contents are the value of the frro
+ * @param configuration The string representation of the config this overlay is enabled for
+ */
+ public Builder setResourceValue(@NonNull String resourceName, ParcelFileDescriptor value,
+ String configuration) {
+ final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+ entry.resourceName = resourceName;
+ entry.binaryData = value;
+ entry.configuration = configuration;
+ mEntries.add(entry);
+ return this;
+ }
+
/** Builds an immutable fabricated overlay. */
public FabricatedOverlay build() {
final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index ff07291..09d24d4 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -40,8 +40,10 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableContainer;
import android.icu.text.PluralRules;
+import android.net.Uri;
import android.os.Build;
import android.os.LocaleList;
+import android.os.ParcelFileDescriptor;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -59,6 +61,8 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
@@ -799,7 +803,21 @@
private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
@NonNull Resources wrapper, @NonNull TypedValue value) {
ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
- wrapper, value);
+ wrapper, value);
+ try {
+ return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (IOException ioe) {
+ // This is okay. This may be something that ImageDecoder does not
+ // support, like SVG.
+ return null;
+ }
+ }
+
+ @Nullable
+ private Drawable decodeImageDrawable(@NonNull FileInputStream fis, @NonNull Resources wrapper) {
+ ImageDecoder.Source src = ImageDecoder.createSource(wrapper, fis);
try {
return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
@@ -860,6 +878,17 @@
} else {
dr = loadXmlDrawable(wrapper, value, id, density, file);
}
+ } else if (file.startsWith("frro://")) {
+ Uri uri = Uri.parse(file);
+ File f = new File('/' + uri.getHost() + uri.getPath());
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ AssetFileDescriptor afd = new AssetFileDescriptor(
+ pfd,
+ Long.parseLong(uri.getQueryParameter("offset")),
+ Long.parseLong(uri.getQueryParameter("size")));
+ FileInputStream is = afd.createInputStream();
+ dr = decodeImageDrawable(is, wrapper);
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index e321a66..b6ff102 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -258,12 +258,14 @@
* waitForService should always be able to return the service.
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @NonNull
public static String[] getDeclaredInstances(@NonNull String iface) {
try {
return getIServiceManager().getDeclaredInstances(iface);
} catch (RemoteException e) {
Log.e(TAG, "error in getDeclaredInstances", e);
- return null;
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8aed2ac..fbfe548 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4392,6 +4392,7 @@
* @return true if the creation of users of the given user type is enabled on this device.
* @hide
*/
+ @TestApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.CREATE_USERS
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 2d29c59..c7a2d24 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -454,6 +454,23 @@
}
/**
+ * Sets whether a container is being drag-resized.
+ * When {@code true}, the client will reuse a single (larger) surface size to avoid
+ * continuous allocations on every size change.
+ *
+ * @param container WindowContainerToken of the task that changed its drag resizing state
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setDragResizing(@NonNull WindowContainerToken container,
+ boolean dragResizing) {
+ final Change change = getOrCreateChange(container.asBinder());
+ change.mChangeMask |= Change.CHANGE_DRAG_RESIZING;
+ change.mDragResizing = dragResizing;
+ return this;
+ }
+
+ /**
* Sends a pending intent in sync.
* @param sender The PendingIntent sender.
* @param intent The fillIn intent to patch over the sender's base intent.
@@ -894,12 +911,14 @@
public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
public static final int CHANGE_FORCE_NO_PIP = 1 << 6;
public static final int CHANGE_FORCE_TRANSLUCENT = 1 << 7;
+ public static final int CHANGE_DRAG_RESIZING = 1 << 8;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
private boolean mHidden = false;
private boolean mIgnoreOrientationRequest = false;
private boolean mForceTranslucent = false;
+ private boolean mDragResizing = false;
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
@@ -920,6 +939,7 @@
mHidden = in.readBoolean();
mIgnoreOrientationRequest = in.readBoolean();
mForceTranslucent = in.readBoolean();
+ mDragResizing = in.readBoolean();
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
@@ -968,6 +988,9 @@
if ((other.mChangeMask & CHANGE_FORCE_TRANSLUCENT) != 0) {
mForceTranslucent = other.mForceTranslucent;
}
+ if ((other.mChangeMask & CHANGE_DRAG_RESIZING) != 0) {
+ mDragResizing = other.mDragResizing;
+ }
mChangeMask |= other.mChangeMask;
if (other.mActivityWindowingMode >= 0) {
mActivityWindowingMode = other.mActivityWindowingMode;
@@ -1027,6 +1050,15 @@
return mForceTranslucent;
}
+ /** Gets the requested drag resizing state. */
+ public boolean getDragResizing() {
+ if ((mChangeMask & CHANGE_DRAG_RESIZING) == 0) {
+ throw new RuntimeException("Drag resizing not set. "
+ + "Check CHANGE_DRAG_RESIZING first");
+ }
+ return mDragResizing;
+ }
+
public int getChangeMask() {
return mChangeMask;
}
@@ -1088,6 +1120,9 @@
if ((mChangeMask & CHANGE_FOCUSABLE) != 0) {
sb.append("focusable:" + mFocusable + ",");
}
+ if ((mChangeMask & CHANGE_DRAG_RESIZING) != 0) {
+ sb.append("dragResizing:" + mDragResizing + ",");
+ }
if (mBoundsChangeTransaction != null) {
sb.append("hasBoundsTransaction,");
}
@@ -1105,6 +1140,7 @@
dest.writeBoolean(mHidden);
dest.writeBoolean(mIgnoreOrientationRequest);
dest.writeBoolean(mForceTranslucent);
+ dest.writeBoolean(mDragResizing);
dest.writeInt(mChangeMask);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 3e988e6..145aeaf 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -2402,7 +2402,7 @@
return;
}
final ThreadedRenderer renderer = getThreadedRenderer();
- if (renderer != null) {
+ if (renderer != null && !CAPTION_ON_SHELL) {
loadBackgroundDrawablesIfNeeded();
WindowInsets rootInsets = getRootWindowInsets();
mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 5b08bb1..6063c90 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -54,6 +54,12 @@
// TODO(b/183140900) split store escrow key errors into detailed ones.
/**
+ * This is called when Weaver is guaranteed to be available (if the device supports Weaver).
+ * It does any synthetic password related work that was delayed from earlier in the boot.
+ */
+ public abstract void onThirdPartyAppsStarted();
+
+ /**
* Unlocks the credential-encrypted storage for the given user if the user is not secured, i.e.
* doesn't have an LSKF.
* <p>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
index f0f2db7..a49a300 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -40,6 +40,9 @@
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mResizeStartPoint = new PointF();
private final Rect mResizeTaskBounds = new Rect();
+ // Whether the |dragResizing| hint should be sent with the next bounds change WCT.
+ // Used to optimized fluid resizing of freeform tasks.
+ private boolean mPendingDragResizeHint = false;
private int mCtrlType;
private DragStartListener mDragStartListener;
@@ -53,6 +56,12 @@
@Override
public void onDragResizeStart(int ctrlType, float x, float y) {
+ if (ctrlType != CTRL_TYPE_UNDEFINED) {
+ // The task is being resized, send the |dragResizing| hint to core with the first
+ // bounds-change wct.
+ mPendingDragResizeHint = true;
+ }
+
mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId);
mCtrlType = ctrlType;
@@ -63,19 +72,31 @@
@Override
public void onDragResizeMove(float x, float y) {
- changeBounds(x, y);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (changeBounds(wct, x, y)) {
+ if (mPendingDragResizeHint) {
+ // This is the first bounds change since drag resize operation started.
+ wct.setDragResizing(mWindowDecoration.mTaskInfo.token, true /* dragResizing */);
+ mPendingDragResizeHint = false;
+ }
+ mTaskOrganizer.applyTransaction(wct);
+ }
}
@Override
public void onDragResizeEnd(float x, float y) {
- changeBounds(x, y);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setDragResizing(mWindowDecoration.mTaskInfo.token, false /* dragResizing */);
+ changeBounds(wct, x, y);
+ mTaskOrganizer.applyTransaction(wct);
mCtrlType = 0;
mTaskBoundsAtDragStart.setEmpty();
mResizeStartPoint.set(0, 0);
+ mPendingDragResizeHint = false;
}
- private void changeBounds(float x, float y) {
+ private boolean changeBounds(WindowContainerTransaction wct, float x, float y) {
float deltaX = x - mResizeStartPoint.x;
mResizeTaskBounds.set(mTaskBoundsAtDragStart);
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
@@ -96,10 +117,10 @@
}
if (!mResizeTaskBounds.isEmpty()) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
- mTaskOrganizer.applyTransaction(wct);
+ return true;
}
+ return false;
}
interface DragStartListener {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
new file mode 100644
index 0000000..ac10ddb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -0,0 +1,130 @@
+package com.android.wm.shell.windowdecor
+
+import android.app.ActivityManager
+import android.graphics.Rect
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [TaskPositioner].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:TaskPositionerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TaskPositionerTest : ShellTestCase() {
+
+ @Mock
+ private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
+ @Mock
+ private lateinit var mockWindowDecoration: WindowDecoration<*>
+ @Mock
+ private lateinit var mockDragStartListener: TaskPositioner.DragStartListener
+
+ @Mock
+ private lateinit var taskToken: WindowContainerToken
+ @Mock
+ private lateinit var taskBinder: IBinder
+
+ private lateinit var taskPositioner: TaskPositioner
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ taskPositioner = TaskPositioner(
+ mockShellTaskOrganizer,
+ mockWindowDecoration,
+ mockDragStartListener
+ )
+ `when`(taskToken.asBinder()).thenReturn(taskBinder)
+ mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
+ taskId = TASK_ID
+ token = taskToken
+ configuration.windowConfiguration.bounds = STARTING_BOUNDS
+ }
+ }
+
+ @Test
+ fun testDragResize_move_skipsDragResizingFlag() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_UNDEFINED, // Move
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ // Move the task 10px to the right.
+ val newX = STARTING_BOUNDS.left.toFloat() + 10
+ val newY = STARTING_BOUNDS.top.toFloat()
+ taskPositioner.onDragResizeMove(
+ newX,
+ newY
+ )
+
+ taskPositioner.onDragResizeEnd(newX, newY)
+
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ change.dragResizing
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_resize_setsDragResizingFlag() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_RIGHT, // Resize right
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ // Resize the task by 10px to the right.
+ val newX = STARTING_BOUNDS.right.toFloat() + 10
+ val newY = STARTING_BOUNDS.top.toFloat()
+ taskPositioner.onDragResizeMove(
+ newX,
+ newY
+ )
+
+ taskPositioner.onDragResizeEnd(newX, newY)
+
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ change.dragResizing
+ }
+ })
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ !change.dragResizing
+ }
+ })
+ }
+
+ companion object {
+ private const val TASK_ID = 5
+ private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+ }
+}
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 58fc5bb..a1385f2 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -35,7 +35,7 @@
using namespace android;
// TODO: This can go away once the only remaining usage in aapt goes away.
-class FileReader : public zip_archive::Reader {
+class FileReader final : public zip_archive::Reader {
public:
explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
}
@@ -66,7 +66,7 @@
mutable off64_t mCurrentOffset;
};
-class FdReader : public zip_archive::Reader {
+class FdReader final : public zip_archive::Reader {
public:
explicit FdReader(int fd) : mFd(fd) {
}
@@ -79,7 +79,7 @@
const int mFd;
};
-class BufferReader : public zip_archive::Reader {
+class BufferReader final : public zip_archive::Reader {
public:
BufferReader(incfs::map_ptr<void> input, size_t inputSize) : Reader(),
mInput(input.convert<uint8_t>()),
@@ -105,7 +105,7 @@
const size_t mInputSize;
};
-class BufferWriter : public zip_archive::Writer {
+class BufferWriter final : public zip_archive::Writer {
public:
BufferWriter(void* output, size_t outputSize) : Writer(),
mOutput(reinterpret_cast<uint8_t*>(output)), mOutputSize(outputSize), mBytesWritten(0) {
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 9309091..a625889 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -53,7 +53,7 @@
// The version should only be changed when a backwards-incompatible change must be made to the
// fabricated overlay file format. Old fabricated overlays must be migrated to the new file format
// to prevent losing fabricated overlay data.
-constexpr const uint32_t kFabricatedOverlayCurrentVersion = 2;
+constexpr const uint32_t kFabricatedOverlayCurrentVersion = 3;
// Returns whether or not the path represents a fabricated overlay.
bool IsFabricatedOverlay(const std::string& path);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 6ae7dfb..9b8ec5e 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -45,6 +45,7 @@
void onRequestTrackInfoList(int seq);
void onRequestCurrentTvInputId(int seq);
void onRequestStartRecording(in Uri programUri, int seq);
+ void onRequestStopRecording(in String recordingId, int seq);
void onRequestSigning(
in String id, in String algorithm, in String alias, in byte[] data, int seq);
void onAdRequest(in AdRequest request, int Seq);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 6478057..4ce5871 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -44,6 +44,7 @@
void onRequestTrackInfoList();
void onRequestCurrentTvInputId();
void onRequestStartRecording(in Uri programUri);
+ void onRequestStopRecording(in String recordingId);
void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data);
void onAdRequest(in AdRequest request);
}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 7d84fb2..0f11407 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -499,6 +499,18 @@
}
@Override
+ public void onRequestStopRecording(String recordingId, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRequestStopRecording(recordingId);
+ }
+ }
+
+ @Override
public void onRequestSigning(
String id, String algorithm, String alias, byte[] data, int seq) {
synchronized (mSessionCallbackRecordMap) {
@@ -1729,6 +1741,15 @@
});
}
+ void postRequestStopRecording(String recordingId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRequestStopRecording(mSession, recordingId);
+ }
+ });
+ }
+
void postRequestSigning(String id, String algorithm, String alias, byte[] data) {
mHandler.post(new Runnable() {
@Override
@@ -1884,11 +1905,22 @@
* called.
*
* @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+ * @param programUri The Uri of the program to be recorded.
*/
public void onRequestStartRecording(Session session, Uri programUri) {
}
/**
+ * This is called when {@link TvInteractiveAppService.Session#RequestStopRecording} is
+ * called.
+ *
+ * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+ * @param recordingId The recordingId of the recording to be stopped.
+ */
+ public void onRequestStopRecording(Session session, String recordingId) {
+ }
+
+ /**
* This is called when
* {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is
* called.
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 2956a0a..9ef6503 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -942,6 +942,33 @@
}
/**
+ * Requests starting of recording
+ *
+ * <p> This is used to request the active {@link android.media.tv.TvRecordingClient} to
+ * call {@link android.media.tv.TvRecordingClient#stopRecording()}.
+ * @see android.media.tv.TvRecordingClient#stopRecording()
+ *
+ * @hide
+ */
+ @CallSuper
+ public void requestStopRecording(@NonNull String recordingId) {
+ executeOrPostRunnableOnMainThread(() -> {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestStopRecording");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onRequestStopRecording(recordingId);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestStopRecording", e);
+ }
+ });
+ }
+
+
+
+ /**
* Requests signing of the given data.
*
* <p>This is used when the corresponding server of the broadcast-independent interactive
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 1f270d0..c21b288 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -867,6 +867,19 @@
}
/**
+ * This is called when {@link TvInteractiveAppService.Session#requestStopRecording()}
+ * is called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @param recordingId The ID of the recording to stop.
+ * @hide
+ */
+ public void onRequestStopRecording(
+ @NonNull String iAppServiceId,
+ @NonNull String recordingId) {
+ }
+
+ /**
* This is called when
* {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is
* called.
@@ -1204,6 +1217,20 @@
}
@Override
+ public void onRequestStopRecording(Session session, String recordingId) {
+ if (DEBUG) {
+ Log.d(TAG, "onRequestStopRecording");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRequestStopRecording - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onRequestStopRecording(mIAppServiceId, recordingId);
+ }
+ }
+
+ @Override
public void onRequestSigning(
Session session, String id, String algorithm, String alias, byte[] data) {
if (DEBUG) {
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 8eafbdf..cb0ba4e 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -7,6 +7,7 @@
leifhendrik@google.com
tmfang@google.com
virgild@google.com
+ykhung@google.com
# Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
per-file *.xml=*
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
index 3e33da5..ece8986 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
@@ -50,6 +50,34 @@
@Override
public void clicked(int sourceCategory, String key) {
+ final LogMaker logMaker = new LogMaker(MetricsProto.MetricsEvent.ACTION_SETTINGS_TILE_CLICK)
+ .setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+ if (sourceCategory != MetricsProto.MetricsEvent.VIEW_UNKNOWN) {
+ logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, sourceCategory);
+ }
+ if (!TextUtils.isEmpty(key)) {
+ logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME,
+ key);
+ }
+ MetricsLogger.action(logMaker);
+ }
+
+ @Override
+ public void changed(int category, String key, int value) {
+ final LogMaker logMaker = new LogMaker(
+ MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE)
+ .setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+ if (category != MetricsProto.MetricsEvent.VIEW_UNKNOWN) {
+ logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_CONTEXT, category);
+ }
+ if (!TextUtils.isEmpty(key)) {
+ logMaker.addTaggedData(MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME,
+ key);
+ logMaker.addTaggedData(
+ MetricsProto.MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_INT_VALUE,
+ value);
+ }
+ MetricsLogger.action(logMaker);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
index cceca13..dcd6cce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
@@ -39,6 +39,11 @@
void clicked(int category, String key);
/**
+ * Logs a value changed event when user changed item value.
+ */
+ void changed(int category, String key, int value);
+
+ /**
* Logs an user action.
*/
void action(Context context, int category, Pair<Integer, Object>... taggedData);
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index 915421a..09abc39 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -108,6 +108,19 @@
}
/**
+ * Logs a value changed event when user changed item value.
+ *
+ * @param category the target page id
+ * @param key the key id that user clicked
+ * @param value the value that user changed which converted to integer
+ */
+ public void changed(int category, String key, int value) {
+ for (LogWriter writer : mLoggerWriters) {
+ writer.changed(category, key, value);
+ }
+ }
+
+ /**
* Logs a simple action without page id or attribution
*
* @param category the target page
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
index 869de0de..067afa4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SharedPreferencesLogger.java
@@ -35,15 +35,22 @@
private static final String LOG_TAG = "SharedPreferencesLogger";
private final String mTag;
+ private final int mMetricCategory;
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeature;
private final Set<String> mPreferenceKeySet;
public SharedPreferencesLogger(Context context, String tag,
MetricsFeatureProvider metricsFeature) {
+ this(context, tag, metricsFeature, SettingsEnums.PAGE_UNKNOWN);
+ }
+
+ public SharedPreferencesLogger(Context context, String tag,
+ MetricsFeatureProvider metricsFeature, int metricCategory) {
mContext = context;
mTag = tag;
mMetricsFeature = metricsFeature;
+ mMetricCategory = metricCategory;
mPreferenceKeySet = new ConcurrentSkipListSet<>();
}
@@ -151,20 +158,15 @@
return;
}
// Pref key exists in set, log its change in metrics.
- mMetricsFeature.action(SettingsEnums.PAGE_UNKNOWN,
- SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
- SettingsEnums.PAGE_UNKNOWN,
- prefKey,
- intVal);
+ mMetricsFeature.changed(mMetricCategory, key, intVal);
}
@VisibleForTesting
void logPackageName(String key, String value) {
- final String prefKey = mTag + "/" + key;
- mMetricsFeature.action(SettingsEnums.PAGE_UNKNOWN,
+ mMetricsFeature.action(mMetricCategory,
SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
SettingsEnums.PAGE_UNKNOWN,
- prefKey + ":" + value,
+ key + ":" + value,
0);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index 34da305..3e710e4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -24,7 +24,6 @@
import android.os.UserHandle;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import android.text.TextUtils;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -221,17 +220,14 @@
}
/**
- * Reverts battery saver schedule mode to none if we are in a bad state where routine mode
- * is selected but no app is configured to actually provide the signal.
+ * Reverts battery saver schedule mode to none if routine mode is selected.
* @param context a valid context
*/
public static void revertScheduleToNoneIfNeeded(Context context) {
ContentResolver resolver = context.getContentResolver();
final int currentMode = Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
- boolean providerConfigured = !TextUtils.isEmpty(context.getString(
- com.android.internal.R.string.config_batterySaverScheduleProvider));
- if (currentMode == PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC && !providerConfigured) {
+ if (currentMode == PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC) {
Global.putInt(resolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE,
PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
index 89de81f..a2b208a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
@@ -39,7 +39,6 @@
private static final String TEST_TAG = "tag";
private static final String TEST_KEY = "key";
- private static final String TEST_TAGGED_KEY = TEST_TAG + "/" + TEST_KEY;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@@ -65,10 +64,8 @@
editor.putInt(TEST_KEY, 2);
editor.putInt(TEST_KEY, 2);
- verify(mMetricsFeature, times(6)).action(eq(SettingsEnums.PAGE_UNKNOWN),
- eq(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
- eq(SettingsEnums.PAGE_UNKNOWN),
- eq(TEST_TAGGED_KEY),
+ verify(mMetricsFeature, times(6)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
+ eq(TEST_KEY),
anyInt());
}
@@ -82,15 +79,11 @@
editor.putBoolean(TEST_KEY, false);
- verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
- SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
- SettingsEnums.PAGE_UNKNOWN,
- TEST_TAGGED_KEY,
+ verify(mMetricsFeature).changed(SettingsEnums.PAGE_UNKNOWN,
+ TEST_KEY,
1);
- verify(mMetricsFeature, times(3)).action(SettingsEnums.PAGE_UNKNOWN,
- SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
- SettingsEnums.PAGE_UNKNOWN,
- TEST_TAGGED_KEY,
+ verify(mMetricsFeature, times(3)).changed(SettingsEnums.PAGE_UNKNOWN,
+ TEST_KEY,
0);
}
@@ -103,10 +96,8 @@
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, 2);
- verify(mMetricsFeature, times(4)).action(eq(SettingsEnums.PAGE_UNKNOWN),
- eq(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
- eq(SettingsEnums.PAGE_UNKNOWN),
- eq(TEST_TAGGED_KEY),
+ verify(mMetricsFeature, times(4)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
+ eq(TEST_KEY),
anyInt());
}
@@ -117,10 +108,8 @@
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, veryBigNumber);
- verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
- SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
- SettingsEnums.PAGE_UNKNOWN,
- TEST_TAGGED_KEY,
+ verify(mMetricsFeature).changed(SettingsEnums.PAGE_UNKNOWN,
+ TEST_KEY,
Integer.MAX_VALUE);
}
@@ -131,10 +120,8 @@
editor.putLong(TEST_KEY, 1);
editor.putLong(TEST_KEY, veryNegativeNumber);
- verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
- SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE,
- SettingsEnums.PAGE_UNKNOWN,
- TEST_TAGGED_KEY, Integer.MIN_VALUE);
+ verify(mMetricsFeature).changed(SettingsEnums.PAGE_UNKNOWN,
+ TEST_KEY, Integer.MIN_VALUE);
}
@Test
@@ -146,10 +133,8 @@
editor.putFloat(TEST_KEY, 1);
editor.putFloat(TEST_KEY, 2);
- verify(mMetricsFeature, times(4)).action(eq(SettingsEnums.PAGE_UNKNOWN),
- eq(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
- eq(SettingsEnums.PAGE_UNKNOWN),
- eq(TEST_TAGGED_KEY),
+ verify(mMetricsFeature, times(4)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
+ eq(TEST_KEY),
anyInt());
}
@@ -159,7 +144,7 @@
verify(mMetricsFeature).action(SettingsEnums.PAGE_UNKNOWN,
ACTION_SETTINGS_PREFERENCE_CHANGE,
SettingsEnums.PAGE_UNKNOWN,
- "tag/key:com.android.settings",
+ "key:com.android.settings",
0);
}
@@ -170,10 +155,8 @@
mSharedPrefLogger.logValue(TEST_KEY, "62");
mSharedPrefLogger.logValue(TEST_KEY, "0");
- verify(mMetricsFeature, times(3)).action(eq(SettingsEnums.PAGE_UNKNOWN),
- eq(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
- eq(SettingsEnums.PAGE_UNKNOWN),
- eq(TEST_TAGGED_KEY),
+ verify(mMetricsFeature, times(3)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
+ eq(TEST_KEY),
anyInt());
}
@@ -185,10 +168,8 @@
mSharedPrefLogger.logValue(TEST_KEY, "4.2");
mSharedPrefLogger.logValue(TEST_KEY, "3.0");
- verify(mMetricsFeature, times(0)).action(eq(SettingsEnums.PAGE_UNKNOWN),
- eq(SettingsEnums.ACTION_SETTINGS_PREFERENCE_CHANGE),
- eq(SettingsEnums.PAGE_UNKNOWN),
- eq(TEST_TAGGED_KEY),
+ verify(mMetricsFeature, times(0)).changed(eq(SettingsEnums.PAGE_UNKNOWN),
+ eq(TEST_KEY),
anyInt());
}
}
diff --git a/packages/SystemUI/res/drawable/internet_dialog_selected_effect.xml b/packages/SystemUI/res/drawable/internet_dialog_selected_effect.xml
new file mode 100644
index 0000000..8f6b4c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_selected_effect.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index ae2537f..f14be41 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -312,22 +312,15 @@
<LinearLayout
android:id="@+id/see_all_layout"
- android:layout_width="match_parent"
+ style="@style/InternetDialog.Network"
android:layout_height="64dp"
- android:clickable="true"
- android:focusable="true"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center_vertical|center_horizontal"
- android:orientation="horizontal"
- android:paddingStart="22dp"
- android:paddingEnd="22dp">
+ android:paddingStart="20dp">
<FrameLayout
android:layout_width="24dp"
android:layout_height="24dp"
android:clickable="false"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+ android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/arrow_forward"
android:src="@drawable/ic_arrow_forward"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 9b8b611..530db0d 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -44,7 +44,7 @@
android:background="@drawable/qs_media_outline_album_bg"
/>
- <com.android.systemui.ripple.MultiRippleView
+ <com.android.systemui.surfaceeffects.ripple.MultiRippleView
android:id="@+id/touch_ripple_view"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_media_session_height_expanded"
@@ -53,6 +53,15 @@
app:layout_constraintTop_toTopOf="@id/album_art"
app:layout_constraintBottom_toBottomOf="@id/album_art" />
+ <com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
+ android:id="@+id/turbulence_noise_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="@id/album_art"
+ app:layout_constraintEnd_toEndOf="@id/album_art"
+ app:layout_constraintTop_toTopOf="@id/album_art"
+ app:layout_constraintBottom_toBottomOf="@id/album_art" />
+
<!-- Guideline for output switcher -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/center_vertical_guideline"
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
index 887e3e7..f1bc883 100644
--- a/packages/SystemUI/res/layout/wireless_charging_layout.xml
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -22,7 +22,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.systemui.ripple.RippleView
+ <com.android.systemui.surfaceeffects.ripple.RippleView
android:id="@+id/wireless_charging_ripple"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4e4bfe2..ae80070 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1091,7 +1091,7 @@
<item name="android:orientation">horizontal</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
- <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:background">@drawable/internet_dialog_selected_effect</item>
</style>
<style name="InternetDialog.NetworkTitle">
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index 148e5ec..1eb621e 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -44,6 +44,16 @@
app:layout_constraintTop_toTopOf="@+id/album_art"
app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+ <!-- Turbulence noise must have the same constraint as the album art. -->
+ <Constraint
+ android:id="@+id/turbulence_noise_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_collapsed"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
<Constraint
android:id="@+id/header_title"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml
index ac484d7..64c2ef1 100644
--- a/packages/SystemUI/res/xml/media_session_expanded.xml
+++ b/packages/SystemUI/res/xml/media_session_expanded.xml
@@ -37,6 +37,16 @@
app:layout_constraintTop_toTopOf="@+id/album_art"
app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+ <!-- Turbulence noise must have the same constraint as the album art. -->
+ <Constraint
+ android:id="@+id/turbulence_noise_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
<Constraint
android:id="@+id/header_title"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index c93fe6a..4b57d45 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -29,7 +29,7 @@
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.animation.Interpolators
-import com.android.systemui.ripple.RippleShader
+import com.android.systemui.surfaceeffects.ripple.RippleShader
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 616e49c..1454210 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -31,7 +31,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.ripple.RippleView
+import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index e82d0ea..3808ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -30,7 +30,7 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.ripple.RippleShader.RippleShape;
+import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
/**
* A WirelessChargingAnimation is a view containing view + animation for wireless charging.
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 1455699..36103f8 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -33,9 +33,9 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.ripple.RippleAnimationConfig;
-import com.android.systemui.ripple.RippleShader.RippleShape;
-import com.android.systemui.ripple.RippleView;
+import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig;
+import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
+import com.android.systemui.surfaceeffects.ripple.RippleView;
import java.text.NumberFormat;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 076e705..8b71580 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -210,9 +210,7 @@
@JvmField val NEW_FOOTER_ACTIONS = releasedFlag(507, "new_footer_actions")
// TODO(b/244064524): Tracking Bug
- @JvmField
- val QS_SECONDARY_DATA_SUB_INFO =
- unreleasedFlag(508, "qs_secondary_data_sub_info", teamfood = true)
+ @JvmField val QS_SECONDARY_DATA_SUB_INFO = releasedFlag(508, "qs_secondary_data_sub_info")
// 600- status bar
// TODO(b/254513246): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
index a7f1b95..a8f39fa9a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
@@ -26,7 +26,8 @@
import androidx.constraintlayout.widget.Barrier
import com.android.systemui.R
import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
private const val TAG = "MediaViewHolder"
@@ -38,6 +39,8 @@
// Player information
val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
val multiRippleView = itemView.requireViewById<MultiRippleView>(R.id.touch_ripple_view)
+ val turbulenceNoiseView =
+ itemView.requireViewById<TurbulenceNoiseView>(R.id.turbulence_noise_view)
val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
val titleText = itemView.requireViewById<TextView>(R.id.header_title)
val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
index 918417f..93be6a7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
@@ -29,7 +29,8 @@
import com.android.settingslib.Utils
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.monet.ColorScheme
-import com.android.systemui.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
/**
* A [ColorTransition] is an object that updates the colors of views each time [updateColorScheme]
@@ -102,13 +103,21 @@
private val context: Context,
private val mediaViewHolder: MediaViewHolder,
private val multiRippleController: MultiRippleController,
+ private val turbulenceNoiseController: TurbulenceNoiseController,
animatingColorTransitionFactory: AnimatingColorTransitionFactory
) {
constructor(
context: Context,
mediaViewHolder: MediaViewHolder,
multiRippleController: MultiRippleController,
- ) : this(context, mediaViewHolder, multiRippleController, ::AnimatingColorTransition)
+ turbulenceNoiseController: TurbulenceNoiseController
+ ) : this(
+ context,
+ mediaViewHolder,
+ multiRippleController,
+ turbulenceNoiseController,
+ ::AnimatingColorTransition
+ )
val bgColor = context.getColor(com.android.systemui.R.color.material_dynamic_secondary95)
val surfaceColor =
@@ -129,6 +138,7 @@
mediaViewHolder.actionPlayPause.backgroundTintList = accentColorList
mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
multiRippleController.updateColor(accentPrimary)
+ turbulenceNoiseController.updateNoiseColor(accentPrimary)
}
val accentSecondary =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 215fa03..4747052 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -31,6 +31,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
+import android.graphics.BlendMode;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
@@ -64,6 +65,7 @@
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.settingslib.widget.AdaptiveIcon;
@@ -97,13 +99,16 @@
import com.android.systemui.monet.Style;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.ripple.MultiRippleController;
-import com.android.systemui.ripple.RippleAnimation;
-import com.android.systemui.ripple.RippleAnimationConfig;
-import com.android.systemui.ripple.RippleShader;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController;
+import com.android.systemui.surfaceeffects.ripple.MultiRippleView;
+import com.android.systemui.surfaceeffects.ripple.RippleAnimation;
+import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig;
+import com.android.systemui.surfaceeffects.ripple.RippleShader;
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig;
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController;
import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.animation.TransitionLayout;
import com.android.systemui.util.time.SystemClock;
@@ -216,7 +221,9 @@
private boolean mShowBroadcastDialogButton = false;
private String mSwitchBroadcastApp;
private MultiRippleController mMultiRippleController;
+ private TurbulenceNoiseController mTurbulenceNoiseController;
private FeatureFlags mFeatureFlags;
+ private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig = null;
/**
* Initialize a new control panel
@@ -394,9 +401,20 @@
AnimatorSet exit = loadAnimator(R.anim.media_metadata_exit,
Interpolators.EMPHASIZED_ACCELERATE, titleText, artistText);
- mMultiRippleController = new MultiRippleController(vh.getMultiRippleView());
+ MultiRippleView multiRippleView = vh.getMultiRippleView();
+ mMultiRippleController = new MultiRippleController(multiRippleView);
+ mTurbulenceNoiseController = new TurbulenceNoiseController(vh.getTurbulenceNoiseView());
+ multiRippleView.addRipplesFinishedListener(
+ () -> {
+ if (mTurbulenceNoiseAnimationConfig == null) {
+ mTurbulenceNoiseAnimationConfig = createLingeringNoiseAnimation();
+ }
+ // Color will be correctly updated in ColorSchemeTransition.
+ mTurbulenceNoiseController.play(mTurbulenceNoiseAnimationConfig);
+ }
+ );
mColorSchemeTransition = new ColorSchemeTransition(
- mContext, mMediaViewHolder, mMultiRippleController);
+ mContext, mMediaViewHolder, mMultiRippleController, mTurbulenceNoiseController);
mMetadataAnimationHandler = new MetadataAnimationHandler(exit, enter);
}
@@ -1027,7 +1045,7 @@
/* maxWidth= */ maxSize,
/* maxHeight= */ maxSize,
/* pixelDensity= */ getContext().getResources().getDisplayMetrics().density,
- mColorSchemeTransition.getAccentPrimary().getTargetColor(),
+ mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
/* opacity= */ 100,
/* shouldFillRipple= */ false,
/* sparkleStrength= */ 0f,
@@ -1036,6 +1054,26 @@
);
}
+ private TurbulenceNoiseAnimationConfig createLingeringNoiseAnimation() {
+ return new TurbulenceNoiseAnimationConfig(
+ TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_GRID_COUNT,
+ TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
+ /* noiseMoveSpeedX= */ 0f,
+ /* noiseMoveSpeedY= */ 0f,
+ TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
+ /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
+ // We want to add (BlendMode.PLUS) the turbulence noise on top of the album art.
+ // Thus, set the background color with alpha 0.
+ /* backgroundColor= */ ColorUtils.setAlphaComponent(Color.BLACK, 0),
+ TurbulenceNoiseAnimationConfig.DEFAULT_OPACITY,
+ /* width= */ mMediaViewHolder.getMultiRippleView().getWidth(),
+ /* height= */ mMediaViewHolder.getMultiRippleView().getHeight(),
+ TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_DURATION_IN_MILLIS,
+ this.getContext().getResources().getDisplayMetrics().density,
+ BlendMode.PLUS,
+ /* onAnimationEnd= */ null
+ );
+ }
private void clearButton(final ImageButton button) {
button.setImageDrawable(null);
button.setContentDescription(null);
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index e354a03..1ea2025 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -18,8 +18,8 @@
import android.content.Context
import android.util.AttributeSet
-import com.android.systemui.ripple.RippleShader
-import com.android.systemui.ripple.RippleView
+import com.android.systemui.surfaceeffects.ripple.RippleShader
+import com.android.systemui.surfaceeffects.ripple.RippleView
/**
* An expanding ripple effect for the media tap-to-transfer receiver chip.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index a895d72..9743c3e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -249,15 +249,7 @@
mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
-
- TypedArray typedArray = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.selectableItemBackground});
- try {
- mBackgroundOff = typedArray.getDrawable(0 /* index */);
- } finally {
- typedArray.recycle();
- }
-
+ mBackgroundOff = mContext.getDrawable(R.drawable.internet_dialog_selected_effect);
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
mAirplaneModeButton.setVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
deleted file mode 100644
index 6de4648..0000000
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2022 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 com.android.systemui.ripple
-
-/** A common utility functions that are used for computing [RippleShader]. */
-class RippleShaderUtilLibrary {
- //language=AGSL
- companion object {
- const val SHADER_LIB = """
- float triangleNoise(vec2 n) {
- n = fract(n * vec2(5.3987, 5.4421));
- n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
- float xy = n.x * n.y;
- return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
- }
- const float PI = 3.1415926535897932384626;
-
- float sparkles(vec2 uv, float t) {
- float n = triangleNoise(uv);
- float s = 0.0;
- for (float i = 0; i < 4; i += 1) {
- float l = i * 0.01;
- float h = l + 0.1;
- float o = smoothstep(n - l, h, n);
- o *= abs(sin(PI * o * (t + 0.55 * i)));
- s += o;
- }
- return s;
- }
-
- vec2 distort(vec2 p, float time, float distort_amount_radial,
- float distort_amount_xy) {
- float angle = atan(p.y, p.x);
- return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
- cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
- + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
- cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
- }"""
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 334f1af..d772378 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -172,7 +172,6 @@
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
-import com.android.systemui.ripple.RippleShader.RippleShape;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
@@ -232,6 +231,7 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt
rename to packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
index 48df15c..93e78ac 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import androidx.annotation.VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
similarity index 76%
rename from packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt
rename to packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
index c7f0b7e..f558fee 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/MultiRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.content.Context
import android.graphics.Canvas
@@ -31,11 +31,21 @@
class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
internal val ripples = ArrayList<RippleAnimation>()
+ private val listeners = ArrayList<RipplesFinishedListener>()
private val ripplePaint = Paint()
private var isWarningLogged = false
companion object {
- const val TAG = "MultiRippleView"
+ private const val TAG = "MultiRippleView"
+
+ interface RipplesFinishedListener {
+ /** Triggered when all the ripples finish running. */
+ fun onRipplesFinish()
+ }
+ }
+
+ fun addRipplesFinishedListener(listener: RipplesFinishedListener) {
+ listeners.add(listener)
}
override fun onDraw(canvas: Canvas?) {
@@ -62,6 +72,10 @@
shouldInvalidate = shouldInvalidate || anim.isPlaying()
}
- if (shouldInvalidate) invalidate()
+ if (shouldInvalidate) {
+ invalidate()
+ } else { // Nothing is playing.
+ listeners.forEach { listener -> listener.onRipplesFinish() }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt
rename to packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
index aca9e25..b2f8994 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt
rename to packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
index 8812254..ae73df2 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleAnimationConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
@@ -1,4 +1,4 @@
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.graphics.Color
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
similarity index 69%
rename from packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
rename to packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
index d2f3a6a..a950d34 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
@@ -13,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.graphics.PointF
import android.graphics.RuntimeShader
import android.util.MathUtils
+import com.android.systemui.surfaceeffects.shaderutil.SdfShaderLibrary
+import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
/**
* Shader class that renders an expanding ripple effect. The ripple contains three elements:
@@ -31,7 +33,7 @@
* Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
*/
class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.CIRCLE) :
- RuntimeShader(buildShader(rippleShape)) {
+ RuntimeShader(buildShader(rippleShape)) {
/** Shapes that the [RippleShader] supports. */
enum class RippleShape {
@@ -39,25 +41,30 @@
ROUNDED_BOX,
ELLIPSE
}
- //language=AGSL
+ // language=AGSL
companion object {
- private const val SHADER_UNIFORMS = """uniform vec2 in_center;
- uniform vec2 in_size;
- uniform float in_progress;
- uniform float in_cornerRadius;
- uniform float in_thickness;
- uniform float in_time;
- uniform float in_distort_radial;
- uniform float in_distort_xy;
- uniform float in_fadeSparkle;
- uniform float in_fadeFill;
- uniform float in_fadeRing;
- uniform float in_blur;
- uniform float in_pixelDensity;
- layout(color) uniform vec4 in_color;
- uniform float in_sparkle_strength;"""
+ private const val SHADER_UNIFORMS =
+ """
+ uniform vec2 in_center;
+ uniform vec2 in_size;
+ uniform float in_progress;
+ uniform float in_cornerRadius;
+ uniform float in_thickness;
+ uniform float in_time;
+ uniform float in_distort_radial;
+ uniform float in_distort_xy;
+ uniform float in_fadeSparkle;
+ uniform float in_fadeFill;
+ uniform float in_fadeRing;
+ uniform float in_blur;
+ uniform float in_pixelDensity;
+ layout(color) uniform vec4 in_color;
+ uniform float in_sparkle_strength;
+ """
- private const val SHADER_CIRCLE_MAIN = """vec4 main(vec2 p) {
+ private const val SHADER_CIRCLE_MAIN =
+ """
+ vec4 main(vec2 p) {
vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
float radius = in_size.x * 0.5;
float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur);
@@ -73,7 +80,9 @@
}
"""
- private const val SHADER_ROUNDED_BOX_MAIN = """vec4 main(vec2 p) {
+ private const val SHADER_ROUNDED_BOX_MAIN =
+ """
+ vec4 main(vec2 p) {
float sparkleRing = soften(roundedBoxRing(p-in_center, in_size, in_cornerRadius,
in_thickness), in_blur);
float inside = soften(sdRoundedBox(p-in_center, in_size * 1.2, in_cornerRadius),
@@ -89,7 +98,9 @@
}
"""
- private const val SHADER_ELLIPSE_MAIN = """vec4 main(vec2 p) {
+ private const val SHADER_ELLIPSE_MAIN =
+ """
+ vec4 main(vec2 p) {
vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
float sparkleRing = soften(ellipseRing(p_distorted-in_center, in_size), in_blur);
@@ -105,22 +116,31 @@
}
"""
- private const val CIRCLE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
- SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.CIRCLE_SDF +
+ private const val CIRCLE_SHADER =
+ SHADER_UNIFORMS +
+ ShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.CIRCLE_SDF +
SHADER_CIRCLE_MAIN
- private const val ROUNDED_BOX_SHADER = SHADER_UNIFORMS +
- RippleShaderUtilLibrary.SHADER_LIB + SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
- SdfShaderLibrary.ROUNDED_BOX_SDF + SHADER_ROUNDED_BOX_MAIN
- private const val ELLIPSE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
- SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.ELLIPSE_SDF +
+ private const val ROUNDED_BOX_SHADER =
+ SHADER_UNIFORMS +
+ ShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.ROUNDED_BOX_SDF +
+ SHADER_ROUNDED_BOX_MAIN
+ private const val ELLIPSE_SHADER =
+ SHADER_UNIFORMS +
+ ShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.ELLIPSE_SDF +
SHADER_ELLIPSE_MAIN
private fun buildShader(rippleShape: RippleShape): String =
- when (rippleShape) {
- RippleShape.CIRCLE -> CIRCLE_SHADER
- RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER
- RippleShape.ELLIPSE -> ELLIPSE_SHADER
- }
+ when (rippleShape) {
+ RippleShape.CIRCLE -> CIRCLE_SHADER
+ RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER
+ RippleShape.ELLIPSE -> ELLIPSE_SHADER
+ }
private fun subProgress(start: Float, end: Float, progress: Float): Float {
val min = Math.min(start, end)
@@ -130,9 +150,7 @@
}
}
- /**
- * Sets the center position of the ripple.
- */
+ /** Sets the center position of the ripple. */
fun setCenter(x: Float, y: Float) {
setFloatUniform("in_center", x, y)
}
@@ -144,21 +162,21 @@
maxSize.y = height
}
- /**
- * Progress of the ripple. Float value between [0, 1].
- */
+ /** Progress of the ripple. Float value between [0, 1]. */
var progress: Float = 0.0f
set(value) {
field = value
setFloatUniform("in_progress", value)
val curvedProg = 1 - (1 - value) * (1 - value) * (1 - value)
- setFloatUniform("in_size", /* width= */ maxSize.x * curvedProg,
- /* height= */ maxSize.y * curvedProg)
+ setFloatUniform(
+ "in_size",
+ /* width= */ maxSize.x * curvedProg,
+ /* height= */ maxSize.y * curvedProg
+ )
setFloatUniform("in_thickness", maxSize.y * curvedProg * 0.5f)
// radius should not exceed width and height values.
- setFloatUniform("in_cornerRadius",
- Math.min(maxSize.x, maxSize.y) * curvedProg)
+ setFloatUniform("in_cornerRadius", Math.min(maxSize.x, maxSize.y) * curvedProg)
setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value))
@@ -175,18 +193,14 @@
setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple))
}
- /**
- * Play time since the start of the effect.
- */
+ /** Play time since the start of the effect. */
var time: Float = 0.0f
set(value) {
field = value
setFloatUniform("in_time", value)
}
- /**
- * A hex value representing the ripple color, in the format of ARGB
- */
+ /** A hex value representing the ripple color, in the format of ARGB */
var color: Int = 0xffffff
set(value) {
field = value
@@ -194,9 +208,9 @@
}
/**
- * Noise sparkle intensity. Expected value between [0, 1]. The sparkle is white, and thus
- * with strength 0 it's transparent, leaving the ripple fully smooth, while with strength 1
- * it's opaque white and looks the most grainy.
+ * Noise sparkle intensity. Expected value between [0, 1]. The sparkle is white, and thus with
+ * strength 0 it's transparent, leaving the ripple fully smooth, while with strength 1 it's
+ * opaque white and looks the most grainy.
*/
var sparkleStrength: Float = 0.0f
set(value) {
@@ -204,9 +218,7 @@
setFloatUniform("in_sparkle_strength", value)
}
- /**
- * Distortion strength of the ripple. Expected value between[0, 1].
- */
+ /** Distortion strength of the ripple. Expected value between[0, 1]. */
var distortionStrength: Float = 0.0f
set(value) {
field = value
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
similarity index 78%
rename from packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
rename to packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
index a6d7930..2994694 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
@@ -26,7 +26,7 @@
import android.util.AttributeSet
import android.view.View
import androidx.core.graphics.ColorUtils
-import com.android.systemui.ripple.RippleShader.RippleShape
+import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape
/**
* A generic expanding ripple effect.
@@ -98,15 +98,18 @@
rippleShader.time = now.toFloat()
invalidate()
}
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- onAnimationEnd?.run()
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd?.run()
+ }
}
- })
+ )
animator.start()
}
- /** Set the color to be used for the ripple.
+ /**
+ * Set the color to be used for the ripple.
*
* The alpha value of the color will be applied to the ripple. The alpha range is [0-100].
*/
@@ -123,9 +126,7 @@
rippleShader.rippleFill = rippleFill
}
- /**
- * Set the intensity of the sparkles.
- */
+ /** Set the intensity of the sparkles. */
fun setSparkleStrength(strength: Float) {
rippleShader.sparkleStrength = strength
}
@@ -143,20 +144,30 @@
// active effect area. Values here should be kept in sync with the animation implementation
// in the ripple shader.
if (rippleShape == RippleShape.CIRCLE) {
- val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * maxWidth
+ val maskRadius =
+ (1 -
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxWidth
canvas.drawCircle(centerX, centerY, maskRadius, ripplePaint)
} else {
- val maskWidth = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * maxWidth * 2
- val maskHeight = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * maxHeight * 2
+ val maskWidth =
+ (1 -
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxWidth * 2
+ val maskHeight =
+ (1 -
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxHeight * 2
canvas.drawRect(
- /* left= */ centerX - maskWidth,
- /* top= */ centerY - maskHeight,
- /* right= */ centerX + maskWidth,
- /* bottom= */ centerY + maskHeight,
- ripplePaint)
+ /* left= */ centerX - maskWidth,
+ /* top= */ centerY - maskHeight,
+ /* right= */ centerX + maskWidth,
+ /* bottom= */ centerY + maskHeight,
+ ripplePaint
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
rename to packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
index 5e256c6..8b2f466 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.shaderutil
/** Library class that contains 2D signed distance functions. */
class SdfShaderLibrary {
- //language=AGSL
+ // language=AGSL
companion object {
- const val CIRCLE_SDF = """
+ const val CIRCLE_SDF =
+ """
float sdCircle(vec2 p, float r) {
return (length(p)-r) / r;
}
@@ -34,7 +35,8 @@
}
"""
- const val ROUNDED_BOX_SDF = """
+ const val ROUNDED_BOX_SDF =
+ """
float sdRoundedBox(vec2 p, vec2 size, float cornerRadius) {
size *= 0.5;
cornerRadius *= 0.5;
@@ -58,7 +60,8 @@
// Used non-trigonometry parametrization and Halley's method (iterative) for root finding.
// This is more expensive than the regular circle SDF, recommend to use the circle SDF if
// possible.
- const val ELLIPSE_SDF = """float sdEllipse(vec2 p, vec2 wh) {
+ const val ELLIPSE_SDF =
+ """float sdEllipse(vec2 p, vec2 wh) {
wh *= 0.5;
// symmetry
@@ -98,7 +101,8 @@
}
"""
- const val SHADER_SDF_OPERATION_LIB = """
+ const val SHADER_SDF_OPERATION_LIB =
+ """
float soften(float d, float blur) {
float blurHalf = blur * 0.5;
return smoothstep(-blurHalf, blurHalf, d);
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
new file mode 100644
index 0000000..d78e0c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.surfaceeffects.shaderutil
+
+/** A common utility functions that are used for computing shaders. */
+class ShaderUtilLibrary {
+ // language=AGSL
+ companion object {
+ const val SHADER_LIB =
+ """
+ float triangleNoise(vec2 n) {
+ n = fract(n * vec2(5.3987, 5.4421));
+ n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
+ float xy = n.x * n.y;
+ // compute in [0..2[ and remap to [-1.0..1.0[
+ return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
+ }
+
+ const float PI = 3.1415926535897932384626;
+
+ float sparkles(vec2 uv, float t) {
+ float n = triangleNoise(uv);
+ float s = 0.0;
+ for (float i = 0; i < 4; i += 1) {
+ float l = i * 0.01;
+ float h = l + 0.1;
+ float o = smoothstep(n - l, h, n);
+ o *= abs(sin(PI * o * (t + 0.55 * i)));
+ s += o;
+ }
+ return s;
+ }
+
+ vec2 distort(vec2 p, float time, float distort_amount_radial,
+ float distort_amount_xy) {
+ float angle = atan(p.y, p.x);
+ return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
+ cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
+ + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
+ cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
+ }
+
+ // Return range [-1, 1].
+ vec3 hash(vec3 p) {
+ p = fract(p * vec3(.3456, .1234, .9876));
+ p += dot(p, p.yxz + 43.21);
+ p = (p.xxy + p.yxx) * p.zyx;
+ return (fract(sin(p) * 4567.1234567) - .5) * 2.;
+ }
+
+ // Skew factors (non-uniform).
+ const float SKEW = 0.3333333; // 1/3
+ const float UNSKEW = 0.1666667; // 1/6
+
+ // Return range roughly [-1,1].
+ // It's because the hash function (that returns a random gradient vector) returns
+ // different magnitude of vectors. Noise doesn't have to be in the precise range thus
+ // skipped normalize.
+ float simplex3d(vec3 p) {
+ // Skew the input coordinate, so that we get squashed cubical grid
+ vec3 s = floor(p + (p.x + p.y + p.z) * SKEW);
+
+ // Unskew back
+ vec3 u = s - (s.x + s.y + s.z) * UNSKEW;
+
+ // Unskewed coordinate that is relative to p, to compute the noise contribution
+ // based on the distance.
+ vec3 c0 = p - u;
+
+ // We have six simplices (in this case tetrahedron, since we are in 3D) that we
+ // could possibly in.
+ // Here, we are finding the correct tetrahedron (simplex shape), and traverse its
+ // four vertices (c0..3) when computing noise contribution.
+ // The way we find them is by comparing c0's x,y,z values.
+ // For example in 2D, we can find the triangle (simplex shape in 2D) that we are in
+ // by comparing x and y values. i.e. x>y lower, x<y, upper triangle.
+ // Same applies in 3D.
+ //
+ // Below indicates the offsets (or offset directions) when c0=(x0,y0,z0)
+ // x0>y0>z0: (1,0,0), (1,1,0), (1,1,1)
+ // x0>z0>y0: (1,0,0), (1,0,1), (1,1,1)
+ // z0>x0>y0: (0,0,1), (1,0,1), (1,1,1)
+ // z0>y0>x0: (0,0,1), (0,1,1), (1,1,1)
+ // y0>z0>x0: (0,1,0), (0,1,1), (1,1,1)
+ // y0>x0>z0: (0,1,0), (1,1,0), (1,1,1)
+ //
+ // The rule is:
+ // * For offset1, set 1 at the max component, otherwise 0.
+ // * For offset2, set 0 at the min component, otherwise 1.
+ // * For offset3, set 1 for all.
+ //
+ // Encode x0-y0, y0-z0, z0-x0 in a vec3
+ vec3 en = c0 - c0.yzx;
+ // Each represents whether x0>y0, y0>z0, z0>x0
+ en = step(vec3(0.), en);
+ // en.zxy encodes z0>x0, x0>y0, y0>x0
+ vec3 offset1 = en * (1. - en.zxy); // find max
+ vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min)
+ vec3 offset3 = vec3(1.);
+
+ vec3 c1 = c0 - offset1 + UNSKEW;
+ vec3 c2 = c0 - offset2 + UNSKEW * 2.;
+ vec3 c3 = c0 - offset3 + UNSKEW * 3.;
+
+ // Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution)
+ //
+ // First compute d^2, squared distance to the point.
+ vec4 w; // w = max(0, r^2 - d^2))
+ w.x = dot(c0, c0);
+ w.y = dot(c1, c1);
+ w.z = dot(c2, c2);
+ w.w = dot(c3, c3);
+
+ // Noise contribution should decay to zero before they cross the simplex boundary.
+ // Usually r^2 is 0.5 or 0.6;
+ // 0.5 ensures continuity but 0.6 increases the visual quality for the application
+ // where discontinuity isn't noticeable.
+ w = max(0.6 - w, 0.);
+
+ // Noise contribution from each point.
+ vec4 nc;
+ nc.x = dot(hash(s), c0);
+ nc.y = dot(hash(s + offset1), c1);
+ nc.z = dot(hash(s + offset2), c2);
+ nc.w = dot(hash(s + offset3), c3);
+
+ nc *= w*w*w*w;
+
+ // Add all the noise contributions.
+ // Should multiply by the possible max contribution to adjust the range in [-1,1].
+ return dot(vec4(32.), nc);
+ }
+ """
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
new file mode 100644
index 0000000..5ac3aad7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.graphics.BlendMode
+import android.graphics.Color
+
+/** Turbulence noise animation configuration. */
+data class TurbulenceNoiseAnimationConfig(
+ /** The number of grids that is used to generate noise. */
+ val gridCount: Float = DEFAULT_NOISE_GRID_COUNT,
+
+ /** Multiplier for the noise luma matte. Increase this for brighter effects. */
+ val luminosityMultiplier: Float = DEFAULT_LUMINOSITY_MULTIPLIER,
+
+ /**
+ * Noise move speed variables.
+ *
+ * Its sign determines the direction; magnitude determines the speed. <ul>
+ * ```
+ * <li> [noiseMoveSpeedX] positive: right to left; negative: left to right.
+ * <li> [noiseMoveSpeedY] positive: bottom to top; negative: top to bottom.
+ * <li> [noiseMoveSpeedZ] its sign doesn't matter much, as it moves in Z direction. Use it
+ * to add turbulence in place.
+ * ```
+ * </ul>
+ */
+ val noiseMoveSpeedX: Float = 0f,
+ val noiseMoveSpeedY: Float = 0f,
+ val noiseMoveSpeedZ: Float = DEFAULT_NOISE_SPEED_Z,
+
+ /** Color of the effect. */
+ var color: Int = DEFAULT_COLOR,
+ /** Background color of the effect. */
+ val backgroundColor: Int = DEFAULT_BACKGROUND_COLOR,
+ val opacity: Int = DEFAULT_OPACITY,
+ val width: Float = 0f,
+ val height: Float = 0f,
+ val duration: Float = DEFAULT_NOISE_DURATION_IN_MILLIS,
+ val pixelDensity: Float = 1f,
+ val blendMode: BlendMode = DEFAULT_BLEND_MODE,
+ val onAnimationEnd: Runnable? = null
+) {
+ companion object {
+ const val DEFAULT_NOISE_DURATION_IN_MILLIS = 7500F
+ const val DEFAULT_LUMINOSITY_MULTIPLIER = 1f
+ const val DEFAULT_NOISE_GRID_COUNT = 1.2f
+ const val DEFAULT_NOISE_SPEED_Z = 0.3f
+ const val DEFAULT_OPACITY = 150 // full opacity is 255.
+ const val DEFAULT_COLOR = Color.WHITE
+ const val DEFAULT_BACKGROUND_COLOR = Color.BLACK
+ val DEFAULT_BLEND_MODE = BlendMode.SRC_OVER
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
new file mode 100644
index 0000000..4c7e5f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.surfaceeffects.turbulencenoise
+
+/** A controller that plays [TurbulenceNoiseView]. */
+class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) {
+ /** Updates the color of the noise. */
+ fun updateNoiseColor(color: Int) {
+ turbulenceNoiseView.updateColor(color)
+ }
+
+ // TODO: add cancel and/ or pause once design requirements become clear.
+ /** Plays [TurbulenceNoiseView] with the given config. */
+ fun play(turbulenceNoiseAnimationConfig: TurbulenceNoiseAnimationConfig) {
+ turbulenceNoiseView.play(turbulenceNoiseAnimationConfig)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
new file mode 100644
index 0000000..19c114d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.graphics.RuntimeShader
+import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
+import java.lang.Float.max
+
+/** Shader that renders turbulence simplex noise, with no octave. */
+class TurbulenceNoiseShader : RuntimeShader(TURBULENCE_NOISE_SHADER) {
+ // language=AGSL
+ companion object {
+ private const val UNIFORMS =
+ """
+ uniform float in_gridNum;
+ uniform vec3 in_noiseMove;
+ uniform vec2 in_size;
+ uniform float in_aspectRatio;
+ uniform float in_opacity;
+ uniform float in_pixelDensity;
+ layout(color) uniform vec4 in_color;
+ layout(color) uniform vec4 in_backgroundColor;
+ """
+
+ private const val SHADER_LIB =
+ """
+ float getLuminosity(vec3 c) {
+ return 0.3*c.r + 0.59*c.g + 0.11*c.b;
+ }
+
+ vec3 maskLuminosity(vec3 dest, float lum) {
+ dest.rgb *= vec3(lum);
+ // Clip back into the legal range
+ dest = clamp(dest, vec3(0.), vec3(1.0));
+ return dest;
+ }
+ """
+
+ private const val MAIN_SHADER =
+ """
+ vec4 main(vec2 p) {
+ vec2 uv = p / in_size.xy;
+ uv.x *= in_aspectRatio;
+
+ vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
+ float luma = simplex3d(noiseP) * in_opacity;
+ vec3 mask = maskLuminosity(in_color.rgb, luma);
+ vec3 color = in_backgroundColor.rgb + mask * 0.6;
+
+ // Add dither with triangle distribution to avoid color banding. Ok to dither in the
+ // shader here as we are in gamma space.
+ float dither = triangleNoise(p * in_pixelDensity) / 255.;
+
+ // The result color should be pre-multiplied, i.e. [R*A, G*A, B*A, A], thus need to
+ // multiply rgb with a to get the correct result.
+ color = (color + dither.rrr) * in_color.a;
+ return vec4(color, in_color.a);
+ }
+ """
+
+ private const val TURBULENCE_NOISE_SHADER =
+ ShaderUtilLibrary.SHADER_LIB + UNIFORMS + SHADER_LIB + MAIN_SHADER
+ }
+
+ /** Sets the number of grid for generating noise. */
+ fun setGridCount(gridNumber: Float = 1.0f) {
+ setFloatUniform("in_gridNum", gridNumber)
+ }
+
+ /**
+ * Sets the pixel density of the screen.
+ *
+ * Used it for noise dithering.
+ */
+ fun setPixelDensity(pixelDensity: Float) {
+ setFloatUniform("in_pixelDensity", pixelDensity)
+ }
+
+ /** Sets the noise color of the effect. */
+ fun setColor(color: Int) {
+ setColorUniform("in_color", color)
+ }
+
+ /** Sets the background color of the effect. */
+ fun setBackgroundColor(color: Int) {
+ setColorUniform("in_backgroundColor", color)
+ }
+
+ /**
+ * Sets the opacity to achieve fade in/ out of the animation.
+ *
+ * Expected value range is [1, 0].
+ */
+ fun setOpacity(opacity: Float) {
+ setFloatUniform("in_opacity", opacity)
+ }
+
+ /** Sets the size of the shader. */
+ fun setSize(width: Float, height: Float) {
+ setFloatUniform("in_size", width, height)
+ setFloatUniform("in_aspectRatio", width / max(height, 0.001f))
+ }
+
+ /** Sets noise move speed in x, y, and z direction. */
+ fun setNoiseMove(x: Float, y: Float, z: Float) {
+ setFloatUniform("in_noiseMove", x, y, z)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
new file mode 100644
index 0000000..8649d59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.core.graphics.ColorUtils
+import java.util.Random
+import kotlin.math.sin
+
+/** View that renders turbulence noise effect. */
+class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
+
+ companion object {
+ private const val MS_TO_SEC = 0.001f
+ private const val TWO_PI = Math.PI.toFloat() * 2f
+ }
+
+ @VisibleForTesting val turbulenceNoiseShader = TurbulenceNoiseShader()
+ private val paint = Paint().apply { this.shader = turbulenceNoiseShader }
+ private val random = Random()
+ private val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f)
+ private var config: TurbulenceNoiseAnimationConfig? = null
+
+ val isPlaying: Boolean
+ get() = animator.isRunning
+
+ init {
+ // Only visible during the animation.
+ visibility = INVISIBLE
+ }
+
+ /** Updates the color during the animation. No-op if there's no animation playing. */
+ fun updateColor(color: Int) {
+ config?.let {
+ it.color = color
+ applyConfig(it)
+ }
+ }
+
+ override fun onDraw(canvas: Canvas?) {
+ if (canvas == null || !canvas.isHardwareAccelerated) {
+ // Drawing with the turbulence noise shader requires hardware acceleration, so skip
+ // if it's unsupported.
+ return
+ }
+
+ canvas.drawPaint(paint)
+ }
+
+ internal fun play(config: TurbulenceNoiseAnimationConfig) {
+ if (isPlaying) {
+ return // Ignore if the animation is playing.
+ }
+ visibility = VISIBLE
+ applyConfig(config)
+
+ // Add random offset to avoid same patterned noise.
+ val offsetX = random.nextFloat()
+ val offsetY = random.nextFloat()
+
+ animator.duration = config.duration.toLong()
+ animator.addUpdateListener { updateListener ->
+ val timeInSec = updateListener.currentPlayTime * MS_TO_SEC
+ // Remap [0,1] to [0, 2*PI]
+ val progress = TWO_PI * updateListener.animatedValue as Float
+
+ turbulenceNoiseShader.setNoiseMove(
+ offsetX + timeInSec * config.noiseMoveSpeedX,
+ offsetY + timeInSec * config.noiseMoveSpeedY,
+ timeInSec * config.noiseMoveSpeedZ
+ )
+
+ // Fade in and out the noise as the animation progress.
+ // TODO: replace it with a better curve
+ turbulenceNoiseShader.setOpacity(sin(TWO_PI - progress) * config.luminosityMultiplier)
+
+ invalidate()
+ }
+
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ visibility = INVISIBLE
+ config.onAnimationEnd?.run()
+ }
+ }
+ )
+ animator.start()
+ }
+
+ private fun applyConfig(config: TurbulenceNoiseAnimationConfig) {
+ this.config = config
+ with(turbulenceNoiseShader) {
+ setGridCount(config.gridCount)
+ setColor(ColorUtils.setAlphaComponent(config.color, config.opacity))
+ setBackgroundColor(config.backgroundColor)
+ setSize(config.width, config.height)
+ setPixelDensity(config.pixelDensity)
+ }
+ paint.blendMode = config.blendMode
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 2af0557..d159714 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -24,7 +24,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.ripple.RippleView
+import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
index a8f4138..a943746 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
@@ -25,7 +25,8 @@
import com.android.systemui.media.controls.models.GutsViewHolder
import com.android.systemui.media.controls.models.player.MediaViewHolder
import com.android.systemui.monet.ColorScheme
-import com.android.systemui.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -62,6 +63,7 @@
@Mock private lateinit var mediaViewHolder: MediaViewHolder
@Mock private lateinit var gutsViewHolder: GutsViewHolder
@Mock private lateinit var multiRippleController: MultiRippleController
+ @Mock private lateinit var turbulenceNoiseController: TurbulenceNoiseController
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
@@ -76,6 +78,7 @@
context,
mediaViewHolder,
multiRippleController,
+ turbulenceNoiseController,
animatingColorTransitionFactory
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 1ad2ca9b..eb1f5e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -78,9 +78,10 @@
import com.android.systemui.media.dialog.MediaOutputDialogFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.ripple.MultiRippleView
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.surfaceeffects.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.KotlinArgumentCaptor
@@ -178,6 +179,7 @@
private lateinit var dismiss: FrameLayout
private lateinit var dismissText: TextView
private lateinit var multiRippleView: MultiRippleView
+ private lateinit var turbulenceNoiseView: TurbulenceNoiseView
private lateinit var session: MediaSession
private lateinit var device: MediaDeviceData
@@ -382,6 +384,7 @@
}
multiRippleView = MultiRippleView(context, null)
+ turbulenceNoiseView = TurbulenceNoiseView(context, null)
whenever(viewHolder.player).thenReturn(view)
whenever(viewHolder.appIcon).thenReturn(appIcon)
@@ -425,6 +428,7 @@
whenever(viewHolder.actionsTopBarrier).thenReturn(actionsTopBarrier)
whenever(viewHolder.multiRippleView).thenReturn(multiRippleView)
+ whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView)
}
/** Initialize elements for the recommendation view holder */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 2ef7312..48a53bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -60,8 +60,8 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.UnreleasedFlag;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -169,8 +169,8 @@
private WifiStateWorker mWifiStateWorker;
@Mock
private SignalStrength mSignalStrength;
- @Mock
- private FeatureFlags mFlags;
+
+ private FakeFeatureFlags mFlags = new FakeFeatureFlags();
private TestableResources mTestableResources;
private InternetDialogController mInternetDialogController;
@@ -221,6 +221,7 @@
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
mInternetDialogController.mActivityStarter = mActivityStarter;
mInternetDialogController.mWifiIconInjector = mWifiIconInjector;
+ mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, false);
}
@After
@@ -410,7 +411,7 @@
@Test
public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
- when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
InternetDialogController spyController = spy(mInternetDialogController);
fakeAirplaneModeEnabled(false);
when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
@@ -767,7 +768,7 @@
@Test
public void getSignalStrengthIcon_differentSubId() {
- when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
InternetDialogController spyController = spy(mInternetDialogController);
Drawable icons = spyController.getSignalStrengthIcon(SUB_ID, mContext, 1, 1, 0, false);
Drawable icons2 = spyController.getSignalStrengthIcon(SUB_ID2, mContext, 1, 1, 0, false);
@@ -777,7 +778,7 @@
@Test
public void getActiveAutoSwitchNonDdsSubId() {
- when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
// active on non-DDS
SubscriptionInfo info = mock(SubscriptionInfo.class);
doReturn(SUB_ID2).when(info).getSubscriptionId();
@@ -813,7 +814,7 @@
@Test
public void getMobileNetworkSummary() {
- when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
InternetDialogController spyController = spy(mInternetDialogController);
doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId();
doReturn(true).when(spyController).isMobileDataEnabled();
@@ -837,7 +838,7 @@
@Test
public void launchMobileNetworkSettings_validSubId() {
- when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
InternetDialogController spyController = spy(mInternetDialogController);
doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId();
spyController.launchMobileNetworkSettings(mDialogLaunchView);
@@ -848,7 +849,7 @@
@Test
public void launchMobileNetworkSettings_invalidSubId() {
- when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
InternetDialogController spyController = spy(mInternetDialogController);
doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.when(spyController).getActiveAutoSwitchNonDdsSubId();
@@ -860,7 +861,7 @@
@Test
public void setAutoDataSwitchMobileDataPolicy() {
- when(mFlags.isEnabled(any(UnreleasedFlag.class))).thenReturn(true);
+ mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
mInternetDialogController.setAutoDataSwitchMobileDataPolicy(SUB_ID, true);
verify(mTelephonyManager).setMobileDataPolicyEnabled(eq(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
index 05512e5..0d19ab1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ripple/MultiRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleControllerTest.kt
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.graphics.Color
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.ripple.MultiRippleController.Companion.MAX_RIPPLE_NUMBER
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController.Companion.MAX_RIPPLE_NUMBER
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt
new file mode 100644
index 0000000..2024d53
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/MultiRippleViewTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.surfaceeffects.ripple
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MultiRippleViewTest : SysuiTestCase() {
+ private val fakeSystemClock = FakeSystemClock()
+ // FakeExecutor is needed to run animator.
+ private val fakeExecutor = FakeExecutor(fakeSystemClock)
+
+ @Test
+ fun onRippleFinishes_triggersRippleFinished() {
+ val multiRippleView = MultiRippleView(context, null)
+ val multiRippleController = MultiRippleController(multiRippleView)
+ val rippleAnimationConfig = RippleAnimationConfig(duration = 1000L)
+
+ var isTriggered = false
+ val listener =
+ object : MultiRippleView.Companion.RipplesFinishedListener {
+ override fun onRipplesFinish() {
+ isTriggered = true
+ }
+ }
+ multiRippleView.addRipplesFinishedListener(listener)
+
+ fakeExecutor.execute {
+ val rippleAnimation = RippleAnimation(rippleAnimationConfig)
+ multiRippleController.play(rippleAnimation)
+
+ fakeSystemClock.advanceTime(rippleAnimationConfig.duration)
+
+ assertThat(isTriggered).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
index 7662282..756397a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleAnimationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.graphics.Color
import android.testing.AndroidTestingRunner
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
index 2d2f4cc..1e5ab7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/ripple/RippleViewTest.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.ripple
+package com.android.systemui.surfaceeffects.ripple
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -21,12 +21,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
@SmallTest
@RunWith(AndroidTestingRunner::class)
class RippleViewTest : SysuiTestCase() {
- @Mock
private lateinit var rippleView: RippleView
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
new file mode 100644
index 0000000..d25c8c1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.graphics.Color
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TurbulenceNoiseControllerTest : SysuiTestCase() {
+ private val fakeSystemClock = FakeSystemClock()
+ // FakeExecutor is needed to run animator.
+ private val fakeExecutor = FakeExecutor(fakeSystemClock)
+
+ @Test
+ fun play_playsTurbulenceNoise() {
+ val config = TurbulenceNoiseAnimationConfig(duration = 1000f)
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+
+ val turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+ fakeExecutor.execute {
+ turbulenceNoiseController.play(config)
+
+ assertThat(turbulenceNoiseView.isPlaying).isTrue()
+
+ fakeSystemClock.advanceTime(config.duration.toLong())
+
+ assertThat(turbulenceNoiseView.isPlaying).isFalse()
+ }
+ }
+
+ @Test
+ fun updateColor_updatesCorrectColor() {
+ val config = TurbulenceNoiseAnimationConfig(duration = 1000f, color = Color.WHITE)
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+ val expectedColor = Color.RED
+
+ val turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+ fakeExecutor.execute {
+ turbulenceNoiseController.play(config)
+
+ turbulenceNoiseView.updateColor(expectedColor)
+
+ fakeSystemClock.advanceTime(config.duration.toLong())
+
+ assertThat(config.color).isEqualTo(expectedColor)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
new file mode 100644
index 0000000..633aac0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseViewTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.surfaceeffects.turbulencenoise
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TurbulenceNoiseViewTest : SysuiTestCase() {
+
+ private val fakeSystemClock = FakeSystemClock()
+ // FakeExecutor is needed to run animator.
+ private val fakeExecutor = FakeExecutor(fakeSystemClock)
+
+ @Test
+ fun play_viewHasCorrectVisibility() {
+ val config = TurbulenceNoiseAnimationConfig(duration = 1000f)
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+
+ fakeExecutor.execute {
+ turbulenceNoiseView.play(config)
+
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.VISIBLE)
+
+ fakeSystemClock.advanceTime(config.duration.toLong())
+
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+ }
+ }
+
+ @Test
+ fun play_playsAnimation() {
+ val config = TurbulenceNoiseAnimationConfig(duration = 1000f)
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+
+ fakeExecutor.execute {
+ turbulenceNoiseView.play(config)
+
+ assertThat(turbulenceNoiseView.isPlaying).isTrue()
+ }
+ }
+
+ @Test
+ fun play_onEnd_triggersOnAnimationEnd() {
+ var animationEnd = false
+ val config =
+ TurbulenceNoiseAnimationConfig(
+ duration = 1000f,
+ onAnimationEnd = { animationEnd = true }
+ )
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+
+ fakeExecutor.execute {
+ turbulenceNoiseView.play(config)
+
+ assertThat(turbulenceNoiseView.isPlaying).isTrue()
+
+ fakeSystemClock.advanceTime(config.duration.toLong())
+
+ assertThat(animationEnd).isTrue()
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d0f245f..7fb3765 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -724,7 +724,7 @@
ServiceRecord r = res.record;
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId,
- allowBackgroundActivityStarts);
+ allowBackgroundActivityStarts, false /* isBindService */);
if (!mAm.mUserController.exists(r.userId)) {
Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
@@ -1931,7 +1931,9 @@
if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
resetFgsRestrictionLocked(r);
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
- r.appInfo.uid, r.intent.getIntent(), r, r.userId, false);
+ r.appInfo.uid, r.intent.getIntent(), r, r.userId,
+ false /* allowBackgroundActivityStarts */,
+ false /* isBindService */);
final String temp = "startForegroundDelayMs:" + delayMs;
if (r.mInfoAllowStartForeground != null) {
r.mInfoAllowStartForeground += "; " + temp;
@@ -1945,7 +1947,9 @@
// The second or later time startForeground() is called after service is
// started. Check for app state again.
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
- r.appInfo.uid, r.intent.getIntent(), r, r.userId, false);
+ r.appInfo.uid, r.intent.getIntent(), r, r.userId,
+ false /* allowBackgroundActivityStarts */,
+ false /* isBindService */);
}
// If the foreground service is not started from TOP process, do not allow it to
// have while-in-use location/camera/microphone access.
@@ -2985,7 +2989,7 @@
}
}
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
- false);
+ false /* allowBackgroundActivityStarts */, true /* isBindService */);
if (s.app != null) {
ProcessServiceRecord servicePsr = s.app.mServices;
@@ -6500,7 +6504,7 @@
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
- boolean allowBackgroundActivityStarts) {
+ boolean allowBackgroundActivityStarts, boolean isBindService) {
r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime();
// Check DeviceConfig flag.
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
@@ -6510,14 +6514,15 @@
if (!r.mAllowWhileInUsePermissionInFgs
|| (r.mAllowStartForeground == REASON_DENIED)) {
final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
- callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts);
+ callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts,
+ isBindService);
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
}
if (r.mAllowStartForeground == REASON_DENIED) {
r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked(
allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
- userId);
+ userId, isBindService);
}
}
}
@@ -6537,9 +6542,10 @@
}
final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
callingPackage, callingPid, callingUid, null /* serviceRecord */,
- false /* allowBackgroundActivityStarts */);
+ false /* allowBackgroundActivityStarts */, false);
@ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked(
- allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */);
+ allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */,
+ false /* isBindService */);
if (allowStartFgs == REASON_DENIED) {
if (canBindingClientStartFgsLocked(callingUid) != null) {
@@ -6559,7 +6565,7 @@
*/
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
int callingPid, int callingUid, @Nullable ServiceRecord targetService,
- boolean allowBackgroundActivityStarts) {
+ boolean allowBackgroundActivityStarts, boolean isBindService) {
int ret = REASON_DENIED;
final int uidState = mAm.getUidStateLocked(callingUid);
@@ -6713,12 +6719,12 @@
shouldAllowFgsWhileInUsePermissionLocked(
clientPackageName,
clientPid, clientUid, null /* serviceRecord */,
- false /* allowBackgroundActivityStarts */);
+ false /* allowBackgroundActivityStarts */, false);
final @ReasonCode int allowStartFgs =
shouldAllowFgsStartForegroundNoBindingCheckLocked(
allowWhileInUse2,
clientPid, clientUid, clientPackageName,
- null /* targetService */);
+ null /* targetService */, false);
if (allowStartFgs != REASON_DENIED) {
return new Pair<>(allowStartFgs, clientPackageName);
} else {
@@ -6750,11 +6756,11 @@
*/
private @ReasonCode int shouldAllowFgsStartForegroundWithBindingCheckLocked(
@ReasonCode int allowWhileInUse, String callingPackage, int callingPid,
- int callingUid, Intent intent, ServiceRecord r, int userId) {
+ int callingUid, Intent intent, ServiceRecord r, int userId, boolean isBindService) {
ActivityManagerService.FgsTempAllowListItem tempAllowListReason =
r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
int ret = shouldAllowFgsStartForegroundNoBindingCheckLocked(allowWhileInUse, callingPid,
- callingUid, callingPackage, r);
+ callingUid, callingPackage, r, isBindService);
String bindFromPackage = null;
if (ret == REASON_DENIED) {
@@ -6789,6 +6795,7 @@
+ "; callerTargetSdkVersion:" + callerTargetSdkVersion
+ "; startForegroundCount:" + r.mStartForegroundCount
+ "; bindFromPackage:" + bindFromPackage
+ + ": isBindService:" + isBindService
+ "]";
if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
r.mLoggedInfoAllowStartForeground = false;
@@ -6799,7 +6806,7 @@
private @ReasonCode int shouldAllowFgsStartForegroundNoBindingCheckLocked(
@ReasonCode int allowWhileInUse, int callingPid, int callingUid, String callingPackage,
- @Nullable ServiceRecord targetService) {
+ @Nullable ServiceRecord targetService, boolean isBindService) {
int ret = allowWhileInUse;
if (ret == REASON_DENIED) {
@@ -6981,10 +6988,12 @@
}
private void logFgsBackgroundStart(ServiceRecord r) {
+ /*
// Only log if FGS is started from background.
if (!isFgsBgStart(r.mAllowStartForeground)) {
return;
}
+ */
if (!r.mLoggedInfoAllowStartForeground) {
final String msg = "Background started FGS: "
+ ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ")
@@ -6996,10 +7005,10 @@
}
Slog.i(TAG, msg);
} else {
- if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
- mAm.mConstants.mFgsStartDeniedLogSampleRate)) {
+ //if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
+ // mAm.mConstants.mFgsStartDeniedLogSampleRate)) {
Slog.wtfQuiet(TAG, msg);
- }
+ //}
Slog.w(TAG, msg);
}
r.mLoggedInfoAllowStartForeground = true;
@@ -7082,7 +7091,7 @@
String callingPackage) {
return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid,
/* targetService */ null,
- /* allowBackgroundActivityStarts */ false)
+ /* allowBackgroundActivityStarts */ false, false)
!= REASON_DENIED;
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 56909e3..dfac82c 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -170,7 +170,7 @@
*/
public long DELAY_NORMAL_MILLIS = DEFAULT_DELAY_NORMAL_MILLIS;
private static final String KEY_DELAY_NORMAL_MILLIS = "bcast_delay_normal_millis";
- private static final long DEFAULT_DELAY_NORMAL_MILLIS = 0;
+ private static final long DEFAULT_DELAY_NORMAL_MILLIS = +500;
/**
* For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts
@@ -178,7 +178,7 @@
*/
public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
private static final String KEY_DELAY_CACHED_MILLIS = "bcast_delay_cached_millis";
- private static final long DEFAULT_DELAY_CACHED_MILLIS = +30_000;
+ private static final long DEFAULT_DELAY_CACHED_MILLIS = +120_000;
/**
* For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 47ca427..739d277 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -164,6 +164,7 @@
private boolean mProcessCached;
private boolean mProcessInstrumented;
+ private boolean mProcessPersistent;
private String mCachedToString;
private String mCachedToShortString;
@@ -323,8 +324,10 @@
this.app = app;
if (app != null) {
setProcessInstrumented(app.getActiveInstrumentation() != null);
+ setProcessPersistent(app.isPersistent());
} else {
setProcessInstrumented(false);
+ setProcessPersistent(false);
}
}
@@ -352,6 +355,17 @@
}
/**
+ * Update if this process is in the "persistent" state, which signals broadcast dispatch should
+ * bypass all pauses or delays to prevent the system from becoming out of sync with itself.
+ */
+ public void setProcessPersistent(boolean persistent) {
+ if (mProcessPersistent != persistent) {
+ mProcessPersistent = persistent;
+ invalidateRunnableAt();
+ }
+ }
+
+ /**
* Return if we know of an actively running "warm" process for this queue.
*/
public boolean isProcessWarm() {
@@ -636,6 +650,7 @@
static final int REASON_MAX_PENDING = 3;
static final int REASON_BLOCKED = 4;
static final int REASON_INSTRUMENTED = 5;
+ static final int REASON_PERSISTENT = 6;
static final int REASON_CONTAINS_FOREGROUND = 10;
static final int REASON_CONTAINS_ORDERED = 11;
static final int REASON_CONTAINS_ALARM = 12;
@@ -652,6 +667,7 @@
REASON_MAX_PENDING,
REASON_BLOCKED,
REASON_INSTRUMENTED,
+ REASON_PERSISTENT,
REASON_CONTAINS_FOREGROUND,
REASON_CONTAINS_ORDERED,
REASON_CONTAINS_ALARM,
@@ -672,6 +688,7 @@
case REASON_MAX_PENDING: return "MAX_PENDING";
case REASON_BLOCKED: return "BLOCKED";
case REASON_INSTRUMENTED: return "INSTRUMENTED";
+ case REASON_PERSISTENT: return "PERSISTENT";
case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND";
case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED";
case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM";
@@ -731,6 +748,9 @@
} else if (mCountManifest > 0) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_MANIFEST;
+ } else if (mProcessPersistent) {
+ mRunnableAt = runnableAt;
+ mRunnableAtReason = REASON_PERSISTENT;
} else if (mProcessCached) {
mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
mRunnableAtReason = REASON_CACHED;
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 6793876..ce78d1b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1453,7 +1453,7 @@
}
BroadcastProcessQueue created = new BroadcastProcessQueue(mConstants, processName, uid);
- created.app = mService.getProcessRecordLocked(processName, uid);
+ created.setProcess(mService.getProcessRecordLocked(processName, uid));
if (leaf == null) {
mProcessQueues.put(uid, created);
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index d2ef479..2ad2077 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -30,6 +30,7 @@
import android.app.ActivityManager;
import android.content.ComponentName;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.Slog;
import android.util.TimeUtils;
@@ -43,6 +44,9 @@
* The state info of the process, including proc state, oom adj score, et al.
*/
final class ProcessStateRecord {
+ // Enable this to trace all OomAdjuster state transitions
+ private static final boolean TRACE_OOM_ADJ = false;
+
private final ProcessRecord mApp;
private final ActivityManagerService mService;
private final ActivityManagerGlobalLock mProcLock;
@@ -916,6 +920,12 @@
@GuardedBy("mService")
void setAdjType(String adjType) {
+ if (TRACE_OOM_ADJ) {
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "oom:" + mApp.processName + "/u" + mApp.uid, 0);
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "oom:" + mApp.processName + "/u" + mApp.uid, adjType, 0);
+ }
mAdjType = adjType;
}
@@ -1153,6 +1163,10 @@
@GuardedBy({"mService", "mProcLock"})
void onCleanupApplicationRecordLSP() {
+ if (TRACE_OOM_ADJ) {
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "oom:" + mApp.processName + "/u" + mApp.uid, 0);
+ }
setHasForegroundActivities(false);
mHasShownUi = false;
mForcingToImportant = null;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 92b685c7..d02faad 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -248,14 +248,14 @@
// Locking order is mUserCreationAndRemovalLock -> mSpManager.
private final Object mUserCreationAndRemovalLock = new Object();
- // These two arrays are only used at boot time. To save memory, they are set to null when
- // PHASE_BOOT_COMPLETED is reached.
+ // These two arrays are only used at boot time. To save memory, they are set to null near the
+ // end of the boot, when onThirdPartyAppsStarted() is called.
@GuardedBy("mUserCreationAndRemovalLock")
private SparseIntArray mEarlyCreatedUsers = new SparseIntArray();
@GuardedBy("mUserCreationAndRemovalLock")
private SparseIntArray mEarlyRemovedUsers = new SparseIntArray();
@GuardedBy("mUserCreationAndRemovalLock")
- private boolean mBootComplete;
+ private boolean mThirdPartyAppsStarted;
// Current password metrics for all secured users on the device. Updated when user unlocks the
// device or changes password. Removed when user is stopped.
@@ -297,16 +297,9 @@
@Override
public void onBootPhase(int phase) {
super.onBootPhase(phase);
- switch (phase) {
- case PHASE_ACTIVITY_MANAGER_READY:
- mLockSettingsService.migrateOldDataAfterSystemReady();
- mLockSettingsService.loadEscrowData();
- break;
- case PHASE_BOOT_COMPLETED:
- mLockSettingsService.bootCompleted();
- break;
- default:
- break;
+ if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ mLockSettingsService.migrateOldDataAfterSystemReady();
+ mLockSettingsService.loadEscrowData();
}
}
@@ -749,8 +742,8 @@
* <p>
* This is primarily needed for users that were removed by Android 13 or earlier, which didn't
* guarantee removal of LSS state as it relied on the {@code ACTION_USER_REMOVED} intent. It is
- * also needed because {@link #removeUser()} delays requests to remove LSS state until the
- * {@code PHASE_BOOT_COMPLETED} boot phase, so they can be lost.
+ * also needed because {@link #removeUser()} delays requests to remove LSS state until Weaver is
+ * guaranteed to be available, so they can be lost.
* <p>
* Stale state is detected by checking whether the user serial number changed. This works
* because user serial numbers are never reused.
@@ -931,7 +924,9 @@
return success;
}
- private void bootCompleted() {
+ // This is called when Weaver is guaranteed to be available (if the device supports Weaver).
+ // It does any synthetic password related work that was delayed from earlier in the boot.
+ private void onThirdPartyAppsStarted() {
synchronized (mUserCreationAndRemovalLock) {
// Handle delayed calls to LSS.removeUser() and LSS.createNewUser().
for (int i = 0; i < mEarlyRemovedUsers.size(); i++) {
@@ -976,7 +971,7 @@
setString("migrated_all_users_to_sp_and_bound_ce", "true", 0);
}
- mBootComplete = true;
+ mThirdPartyAppsStarted = true;
}
}
@@ -2304,14 +2299,14 @@
private void createNewUser(@UserIdInt int userId, int userSerialNumber) {
synchronized (mUserCreationAndRemovalLock) {
- // Before PHASE_BOOT_COMPLETED, don't actually create the synthetic password yet, but
- // rather automatically delay it to later. We do this because protecting the synthetic
+ // During early boot, don't actually create the synthetic password yet, but rather
+ // automatically delay it to later. We do this because protecting the synthetic
// password requires the Weaver HAL if the device supports it, and some devices don't
// make Weaver available until fairly late in the boot process. This logic ensures a
// consistent flow across all devices, regardless of their Weaver implementation.
- if (!mBootComplete) {
- Slogf.i(TAG, "Delaying locksettings state creation for user %d until boot complete",
- userId);
+ if (!mThirdPartyAppsStarted) {
+ Slogf.i(TAG, "Delaying locksettings state creation for user %d until third-party " +
+ "apps are started", userId);
mEarlyCreatedUsers.put(userId, userSerialNumber);
mEarlyRemovedUsers.delete(userId);
return;
@@ -2325,14 +2320,14 @@
private void removeUser(@UserIdInt int userId) {
synchronized (mUserCreationAndRemovalLock) {
- // Before PHASE_BOOT_COMPLETED, don't actually remove the LSS state yet, but rather
- // automatically delay it to later. We do this because deleting synthetic password
- // protectors requires the Weaver HAL if the device supports it, and some devices don't
- // make Weaver available until fairly late in the boot process. This logic ensures a
- // consistent flow across all devices, regardless of their Weaver implementation.
- if (!mBootComplete) {
- Slogf.i(TAG, "Delaying locksettings state removal for user %d until boot complete",
- userId);
+ // During early boot, don't actually remove the LSS state yet, but rather automatically
+ // delay it to later. We do this because deleting synthetic password protectors
+ // requires the Weaver HAL if the device supports it, and some devices don't make Weaver
+ // available until fairly late in the boot process. This logic ensures a consistent
+ // flow across all devices, regardless of their Weaver implementation.
+ if (!mThirdPartyAppsStarted) {
+ Slogf.i(TAG, "Delaying locksettings state removal for user %d until third-party " +
+ "apps are started", userId);
if (mEarlyCreatedUsers.indexOfKey(userId) >= 0) {
mEarlyCreatedUsers.delete(userId);
} else {
@@ -2634,9 +2629,8 @@
* protects the user's CE key with a key derived from the SP.
* <p>
* This is called just once in the lifetime of the user: at user creation time (possibly delayed
- * until {@code PHASE_BOOT_COMPLETED} to ensure that the Weaver HAL is available if the device
- * supports it), or when upgrading from Android 13 or earlier where users with no LSKF didn't
- * necessarily have an SP.
+ * until the time when Weaver is guaranteed to be available), or when upgrading from Android 13
+ * or earlier where users with no LSKF didn't necessarily have an SP.
*/
@GuardedBy("mSpManager")
@VisibleForTesting
@@ -3159,7 +3153,7 @@
pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size());
synchronized (mUserCreationAndRemovalLock) {
- pw.println("BootComplete: " + mBootComplete);
+ pw.println("ThirdPartyAppsStarted: " + mThirdPartyAppsStarted);
}
}
@@ -3317,6 +3311,11 @@
private final class LocalService extends LockSettingsInternal {
@Override
+ public void onThirdPartyAppsStarted() {
+ LockSettingsService.this.onThirdPartyAppsStarted();
+ }
+
+ @Override
public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
LockSettingsService.this.unlockUserKeyIfUnsecured(userId);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index 5e98cc0..978e436 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -29,6 +29,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Binder;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ShellCommand;
@@ -64,7 +65,8 @@
private final IOverlayManager mInterface;
private static final Map<String, Integer> TYPE_MAP = Map.of(
"color", TypedValue.TYPE_FIRST_COLOR_INT,
- "string", TypedValue.TYPE_STRING);
+ "string", TypedValue.TYPE_STRING,
+ "drawable", -1);
OverlayManagerShellCommand(@NonNull final Context ctx, @NonNull final IOverlayManager iom) {
mContext = ctx;
@@ -258,7 +260,7 @@
String name = "";
String filename = null;
String opt;
- String configuration = null;
+ String config = null;
while ((opt = getNextOption()) != null) {
switch (opt) {
case "--user":
@@ -277,7 +279,7 @@
filename = getNextArgRequired();
break;
case "--config":
- configuration = getNextArgRequired();
+ config = getNextArgRequired();
break;
default:
err.println("Error: Unknown option: " + opt);
@@ -312,7 +314,9 @@
final String resourceName = getNextArgRequired();
final String typeStr = getNextArgRequired();
final String strData = String.join(" ", peekRemainingArgs());
- addOverlayValue(overlayBuilder, resourceName, typeStr, strData, configuration);
+ if (addOverlayValue(overlayBuilder, resourceName, typeStr, strData, config) != 0) {
+ return 1;
+ }
}
mInterface.commit(new OverlayManagerTransaction.Builder()
@@ -369,8 +373,10 @@
return 1;
}
String config = parser.getAttributeValue(null, "config");
- addOverlayValue(overlayBuilder, targetPackage + ':' + target,
- overlayType, value, config);
+ if (addOverlayValue(overlayBuilder, targetPackage + ':' + target,
+ overlayType, value, config) != 0) {
+ return 1;
+ }
}
}
}
@@ -384,7 +390,7 @@
return 0;
}
- private void addOverlayValue(FabricatedOverlay.Builder overlayBuilder,
+ private int addOverlayValue(FabricatedOverlay.Builder overlayBuilder,
String resourceName, String typeString, String valueString, String configuration) {
final int type;
typeString = typeString.toLowerCase(Locale.getDefault());
@@ -399,6 +405,9 @@
}
if (type == TypedValue.TYPE_STRING) {
overlayBuilder.setResourceValue(resourceName, type, valueString, configuration);
+ } else if (type < 0) {
+ ParcelFileDescriptor pfd = openFileForSystem(valueString, "r");
+ overlayBuilder.setResourceValue(resourceName, pfd, configuration);
} else {
final int intData;
if (valueString.startsWith("0x")) {
@@ -408,6 +417,7 @@
}
overlayBuilder.setResourceValue(resourceName, type, intData, configuration);
}
+ return 0;
}
private int runEnableExclusive() throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java
index 3b676c65..b4792c6 100644
--- a/services/core/java/com/android/server/pm/AppsFilterBase.java
+++ b/services/core/java/com/android/server/pm/AppsFilterBase.java
@@ -44,7 +44,6 @@
import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watched;
-import com.android.server.utils.WatchedArrayList;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedArraySet;
import com.android.server.utils.WatchedSparseBooleanMatrix;
@@ -179,9 +178,9 @@
@NonNull
@Watched
- protected WatchedArrayList<String> mProtectedBroadcasts;
+ protected WatchedArraySet<String> mProtectedBroadcasts;
@NonNull
- protected SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot;
+ protected SnapshotCache<WatchedArraySet<String>> mProtectedBroadcastsSnapshot;
/**
* This structure maps uid -> uid and indicates whether access from the first should be
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 2e67bf2..c97711b 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -73,7 +73,6 @@
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
-import com.android.server.utils.WatchedArrayList;
import com.android.server.utils.WatchedArraySet;
import com.android.server.utils.WatchedSparseBooleanMatrix;
import com.android.server.utils.WatchedSparseSetArray;
@@ -223,7 +222,7 @@
mForceQueryable = new WatchedArraySet<>();
mForceQueryableSnapshot = new SnapshotCache.Auto<>(
mForceQueryable, mForceQueryable, "AppsFilter.mForceQueryable");
- mProtectedBroadcasts = new WatchedArrayList<>();
+ mProtectedBroadcasts = new WatchedArraySet<>();
mProtectedBroadcastsSnapshot = new SnapshotCache.Auto<>(
mProtectedBroadcasts, mProtectedBroadcasts, "AppsFilter.mProtectedBroadcasts");
mPermissionToUids = new HashMap<>();
@@ -573,13 +572,17 @@
return null;
}
- final boolean protectedBroadcastsChanged;
- synchronized (mProtectedBroadcastsLock) {
- protectedBroadcastsChanged =
- mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts());
- }
- if (protectedBroadcastsChanged) {
- mQueriesViaComponentRequireRecompute.set(true);
+ final List<String> newBroadcasts = newPkg.getProtectedBroadcasts();
+ if (newBroadcasts.size() != 0) {
+ final boolean protectedBroadcastsChanged;
+ synchronized (mProtectedBroadcastsLock) {
+ final int oldSize = mProtectedBroadcasts.size();
+ mProtectedBroadcasts.addAll(newBroadcasts);
+ protectedBroadcastsChanged = mProtectedBroadcasts.size() != oldSize;
+ }
+ if (protectedBroadcastsChanged) {
+ mQueriesViaComponentRequireRecompute.set(true);
+ }
}
final boolean newIsForceQueryable;
@@ -1149,7 +1152,12 @@
final ArrayList<String> protectedBroadcasts = new ArrayList<>(
mProtectedBroadcasts.untrackedStorage());
collectProtectedBroadcasts(settings, removingPackageName);
- protectedBroadcastsChanged = !mProtectedBroadcasts.containsAll(protectedBroadcasts);
+ for (int i = 0; i < protectedBroadcasts.size(); ++i) {
+ if (!mProtectedBroadcasts.contains(protectedBroadcasts.get(i))) {
+ protectedBroadcastsChanged = true;
+ break;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/AppsFilterUtils.java b/services/core/java/com/android/server/pm/AppsFilterUtils.java
index 7daa0b9..483fa8a 100644
--- a/services/core/java/com/android/server/pm/AppsFilterUtils.java
+++ b/services/core/java/com/android/server/pm/AppsFilterUtils.java
@@ -29,7 +29,7 @@
import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.component.ParsedProvider;
-import com.android.server.utils.WatchedArrayList;
+import com.android.server.utils.WatchedArraySet;
import java.util.List;
import java.util.Set;
@@ -45,7 +45,7 @@
/** Returns true if the querying package may query for the potential target package */
public static boolean canQueryViaComponents(AndroidPackage querying,
- AndroidPackage potentialTarget, WatchedArrayList<String> protectedBroadcasts) {
+ AndroidPackage potentialTarget, WatchedArraySet<String> protectedBroadcasts) {
if (!querying.getQueriesIntents().isEmpty()) {
for (Intent intent : querying.getQueriesIntents()) {
if (matchesPackage(intent, potentialTarget, protectedBroadcasts)) {
@@ -117,7 +117,7 @@
}
private static boolean matchesPackage(Intent intent, AndroidPackage potentialTarget,
- WatchedArrayList<String> protectedBroadcasts) {
+ WatchedArraySet<String> protectedBroadcasts) {
if (matchesAnyComponents(
intent, potentialTarget.getServices(), null /*protectedBroadcasts*/)) {
return true;
@@ -138,7 +138,7 @@
private static boolean matchesAnyComponents(Intent intent,
List<? extends ParsedMainComponent> components,
- WatchedArrayList<String> protectedBroadcasts) {
+ WatchedArraySet<String> protectedBroadcasts) {
for (int i = ArrayUtils.size(components) - 1; i >= 0; i--) {
ParsedMainComponent component = components.get(i);
if (!component.isExported()) {
@@ -152,7 +152,7 @@
}
private static boolean matchesAnyFilter(Intent intent, ParsedComponent component,
- WatchedArrayList<String> protectedBroadcasts) {
+ WatchedArraySet<String> protectedBroadcasts) {
List<ParsedIntentInfo> intents = component.getIntents();
for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) {
IntentFilter intentFilter = intents.get(i).getIntentFilter();
@@ -164,7 +164,7 @@
}
private static boolean matchesIntentFilter(Intent intent, IntentFilter intentFilter,
- @Nullable WatchedArrayList<String> protectedBroadcasts) {
+ @Nullable WatchedArraySet<String> protectedBroadcasts) {
return intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
intent.getData(), intent.getCategories(), "AppsFilter", true,
protectedBroadcasts != null ? protectedBroadcasts.untrackedStorage() : null) > 0;
diff --git a/services/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java b/services/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java
index d9f504e..ac97038 100644
--- a/services/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java
+++ b/services/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java
@@ -206,16 +206,21 @@
Log.d(TAG, "[" + mTag + "] binding to " + mBoundServiceInfo);
}
+ mRebinder = null;
+
Intent bindIntent = new Intent(mBoundServiceInfo.getAction()).setComponent(
mBoundServiceInfo.getComponentName());
- if (!mContext.bindServiceAsUser(bindIntent, this,
- BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
- mHandler, UserHandle.of(mBoundServiceInfo.getUserId()))) {
- Log.e(TAG, "[" + mTag + "] unexpected bind failure - retrying later");
- mRebinder = this::bind;
- mHandler.postDelayed(mRebinder, RETRY_DELAY_MS);
- } else {
- mRebinder = null;
+ try {
+ if (!mContext.bindServiceAsUser(bindIntent, this,
+ BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
+ mHandler, UserHandle.of(mBoundServiceInfo.getUserId()))) {
+ Log.e(TAG, "[" + mTag + "] unexpected bind failure - retrying later");
+ mRebinder = this::bind;
+ mHandler.postDelayed(mRebinder, RETRY_DELAY_MS);
+ }
+ } catch (SecurityException e) {
+ // if anything goes wrong it shouldn't crash the system server
+ Log.e(TAG, "[" + mTag + "] " + mBoundServiceInfo + " bind failed", e);
}
}
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index f52f0b7..dbddb41 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -2253,6 +2253,23 @@
}
@Override
+ public void onRequestStopRecording(String recordingId) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onRequestStopRecording");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onRequestStopRecording(recordingId, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onRequestStopRecording", e);
+ }
+ }
+ }
+
+ @Override
public void onRequestSigning(String id, String algorithm, String alias, byte[] data) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 872542a..dfcad48 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -291,6 +291,12 @@
private final IBinder mFragmentToken;
/**
+ * Whether to delay the call to {@link #updateOrganizedTaskFragmentSurface()} when there is a
+ * configuration change.
+ */
+ private boolean mDelayOrganizedTaskFragmentSurfaceUpdate;
+
+ /**
* Whether to delay the last activity of TaskFragment being immediately removed while finishing.
* This should only be set on a embedded TaskFragment, where the organizer can have the
* opportunity to perform animations and finishing the adjacent TaskFragment.
@@ -2264,35 +2270,41 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
- // Task will animate differently.
- if (mTaskFragmentOrganizer != null) {
- mTmpPrevBounds.set(getBounds());
- }
-
super.onConfigurationChanged(newParentConfig);
- final boolean shouldStartChangeTransition = shouldStartChangeTransition(mTmpPrevBounds);
- if (shouldStartChangeTransition) {
- initializeChangeTransition(mTmpPrevBounds);
- }
if (mTaskFragmentOrganizer != null) {
- if (mTransitionController.isShellTransitionsEnabled()
- && !mTransitionController.isCollecting(this)) {
- // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
- // update the surface here if it is not collected by Shell transition.
- updateOrganizedTaskFragmentSurface();
- } else if (!mTransitionController.isShellTransitionsEnabled()
- && !shouldStartChangeTransition) {
- // Update the surface here instead of in the organizer so that we can make sure
- // it can be synced with the surface freezer for legacy app transition.
- updateOrganizedTaskFragmentSurface();
- }
+ updateOrganizedTaskFragmentSurface();
}
sendTaskFragmentInfoChanged();
}
+ void deferOrganizedTaskFragmentSurfaceUpdate() {
+ mDelayOrganizedTaskFragmentSurfaceUpdate = true;
+ }
+
+ void continueOrganizedTaskFragmentSurfaceUpdate() {
+ mDelayOrganizedTaskFragmentSurfaceUpdate = false;
+ updateOrganizedTaskFragmentSurface();
+ }
+
private void updateOrganizedTaskFragmentSurface() {
+ if (mDelayOrganizedTaskFragmentSurfaceUpdate) {
+ return;
+ }
+ if (mTransitionController.isShellTransitionsEnabled()
+ && !mTransitionController.isCollecting(this)) {
+ // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
+ // update the surface here if it is not collected by Shell transition.
+ updateOrganizedTaskFragmentSurfaceUnchecked();
+ } else if (!mTransitionController.isShellTransitionsEnabled() && !isAnimating()) {
+ // Update the surface here instead of in the organizer so that we can make sure
+ // it can be synced with the surface freezer for legacy app transition.
+ updateOrganizedTaskFragmentSurfaceUnchecked();
+ }
+ }
+
+ private void updateOrganizedTaskFragmentSurfaceUnchecked() {
final SurfaceControl.Transaction t = getSyncTransaction();
updateSurfacePosition(t);
updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
@@ -2346,7 +2358,7 @@
}
/** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
- private boolean shouldStartChangeTransition(Rect startBounds) {
+ boolean shouldStartChangeTransition(Rect startBounds) {
if (mTaskFragmentOrganizer == null || !canStartChangeTransition()) {
return false;
}
@@ -2366,7 +2378,7 @@
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);
if (mTaskFragmentOrganizer != null) {
- updateOrganizedTaskFragmentSurface();
+ updateOrganizedTaskFragmentSurfaceUnchecked();
// If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to
// emit the callbacks now.
sendTaskFragmentAppeared();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 37bef3a..25df511 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -423,7 +423,7 @@
Transition newTransition = null;
if (isCollecting()) {
if (displayChange != null) {
- throw new IllegalArgumentException("Provided displayChange for a non-new request");
+ Slog.e(TAG, "Provided displayChange for a non-new request", new Throwable());
}
// Make the collecting transition wait until this request is ready.
mCollectingTransition.setReady(readyGroupRef, false);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 7415376..de12a4e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -48,6 +48,7 @@
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -144,6 +145,8 @@
@VisibleForTesting
final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
+ private final Rect mTmpBounds = new Rect();
+
WindowOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
mGlobalLock = atm.mGlobalLock;
@@ -702,7 +705,7 @@
}
private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
- int effects = 0;
+ int effects = applyChanges(tr, c, null /* errorCallbackToken */);
final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
@@ -717,6 +720,10 @@
effects = TRANSACT_EFFECTS_LIFECYCLE;
}
+ if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING) != 0) {
+ tr.setDragResizing(c.getDragResizing(), DRAG_RESIZE_MODE_FREEFORM);
+ }
+
final int childWindowingMode = c.getActivityWindowingMode();
if (childWindowingMode > -1) {
tr.forAllActivities(a -> { a.setWindowingMode(childWindowingMode); });
@@ -759,6 +766,7 @@
private int applyDisplayAreaChanges(DisplayArea displayArea,
WindowContainerTransaction.Change c) {
final int[] effects = new int[1];
+ effects[0] = applyChanges(displayArea, c, null /* errorCallbackToken */);
if ((c.getChangeMask()
& WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
@@ -779,6 +787,27 @@
return effects[0];
}
+ private int applyTaskFragmentChanges(@NonNull TaskFragment taskFragment,
+ @NonNull WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) {
+ if (taskFragment.isEmbeddedTaskFragmentInPip()) {
+ // No override from organizer for embedded TaskFragment in a PIP Task.
+ return 0;
+ }
+
+ // When the TaskFragment is resized, we may want to create a change transition for it, for
+ // which we want to defer the surface update until we determine whether or not to start
+ // change transition.
+ mTmpBounds.set(taskFragment.getBounds());
+ taskFragment.deferOrganizedTaskFragmentSurfaceUpdate();
+ final int effects = applyChanges(taskFragment, c, errorCallbackToken);
+ if (taskFragment.shouldStartChangeTransition(mTmpBounds)) {
+ taskFragment.initializeChangeTransition(mTmpBounds);
+ }
+ taskFragment.continueOrganizedTaskFragmentSurfaceUpdate();
+ mTmpBounds.set(0, 0, 0, 0);
+ return effects;
+ }
+
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
@@ -1444,20 +1473,15 @@
private int applyWindowContainerChange(WindowContainer wc,
WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) {
sanitizeWindowContainer(wc);
- if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) {
- // No override from organizer for embedded TaskFragment in a PIP Task.
- return 0;
+ if (wc.asDisplayArea() != null) {
+ return applyDisplayAreaChanges(wc.asDisplayArea(), c);
+ } else if (wc.asTask() != null) {
+ return applyTaskChanges(wc.asTask(), c);
+ } else if (wc.asTaskFragment() != null) {
+ return applyTaskFragmentChanges(wc.asTaskFragment(), c, errorCallbackToken);
+ } else {
+ return applyChanges(wc, c, errorCallbackToken);
}
-
- int effects = applyChanges(wc, c, errorCallbackToken);
-
- if (wc instanceof DisplayArea) {
- effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c);
- } else if (wc instanceof Task) {
- effects |= applyTaskChanges(wc.asTask(), c);
- }
-
- return effects;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5c5c703..86dd0b5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1540,10 +1540,11 @@
mWmService.makeWindowFreezingScreenIfNeededLocked(this);
// If the orientation is changing, or we're starting or ending a drag resizing action,
- // then we need to hold off on unfreezing the display until this window has been
- // redrawn; to do that, we need to go through the process of getting informed by the
- // application when it has finished drawing.
- if (getOrientationChanging() || dragResizingChanged) {
+ // or we're resizing an embedded Activity, then we need to hold off on unfreezing the
+ // display until this window has been redrawn; to do that, we need to go through the
+ // process of getting informed by the application when it has finished drawing.
+ if (getOrientationChanging() || dragResizingChanged
+ || isEmbeddedActivityResizeChanged()) {
if (dragResizingChanged) {
ProtoLog.v(WM_DEBUG_RESIZE,
"Resize start waiting for draw, "
@@ -4160,6 +4161,20 @@
return mActivityRecord == null || mActivityRecord.isFullyTransparentBarAllowed(frame);
}
+ /**
+ * Whether this window belongs to a resizing embedded activity.
+ */
+ private boolean isEmbeddedActivityResizeChanged() {
+ if (mActivityRecord == null || !isVisibleRequested()) {
+ // No need to update if the window is in the background.
+ return false;
+ }
+
+ final TaskFragment embeddedTaskFragment = mActivityRecord.getOrganizedTaskFragment();
+ return embeddedTaskFragment != null
+ && mDisplayContent.mChangingContainers.contains(embeddedTaskFragment);
+ }
+
boolean isDragResizeChanged() {
return mDragResizing != computeDragResizing();
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fe2d0be..d406e30 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
+import com.android.internal.widget.LockSettingsInternal;
import com.android.server.am.ActivityManagerService;
import com.android.server.ambientcontext.AmbientContextManagerService;
import com.android.server.appbinding.AppBindingService;
@@ -3019,6 +3020,14 @@
t.traceEnd();
}, t);
+ t.traceBegin("LockSettingsThirdPartyAppsStarted");
+ LockSettingsInternal lockSettingsInternal =
+ LocalServices.getService(LockSettingsInternal.class);
+ if (lockSettingsInternal != null) {
+ lockSettingsInternal.onThirdPartyAppsStarted();
+ }
+ t.traceEnd();
+
t.traceBegin("StartSystemUI");
try {
startSystemUi(context, windowManagerF);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 83f1789..3ff2c0e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -118,10 +118,13 @@
doReturn(true).when(mTaskFragment).isVisibleRequested();
clearInvocations(mTransaction);
+ mTaskFragment.deferOrganizedTaskFragmentSurfaceUpdate();
mTaskFragment.setBounds(endBounds);
+ assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds));
+ mTaskFragment.initializeChangeTransition(startBounds);
+ mTaskFragment.continueOrganizedTaskFragmentSurfaceUpdate();
// Surface reset when prepare transition.
- verify(mTaskFragment).initializeChangeTransition(startBounds);
verify(mTransaction).setPosition(mLeash, 0, 0);
verify(mTransaction).setWindowCrop(mLeash, 0, 0);
@@ -166,7 +169,7 @@
mTaskFragment.setBounds(endBounds);
- verify(mTaskFragment, never()).initializeChangeTransition(any());
+ assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 6bd3412..1b888f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -95,6 +95,8 @@
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizer;
import androidx.test.filters.SmallTest;
@@ -800,6 +802,39 @@
}
@Test
+ public void testEmbeddedActivityResizing_clearAllDrawn() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
+ final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION, embeddedActivity,
+ "App window");
+ doReturn(true).when(embeddedActivity).isVisible();
+ embeddedActivity.mVisibleRequested = true;
+ makeWindowVisible(win);
+ win.mLayoutSeq = win.getDisplayContent().mLayoutSeq;
+ // Set the bounds twice:
+ // 1. To make sure there is no orientation change after #reportResized, which can also cause
+ // #clearAllDrawn.
+ // 2. Make #isLastConfigReportedToClient to be false after #reportResized, so it can process
+ // to check if we need redraw.
+ embeddedTf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ embeddedTf.setBounds(0, 0, 1000, 2000);
+ win.reportResized();
+ embeddedTf.setBounds(500, 0, 1000, 2000);
+
+ // Clear all drawn when the embedded TaskFragment is in mDisplayContent.mChangingContainers.
+ win.updateResizingWindowIfNeeded();
+ verify(embeddedActivity, never()).clearAllDrawn();
+
+ mDisplayContent.mChangingContainers.add(embeddedTf);
+ win.updateResizingWindowIfNeeded();
+ verify(embeddedActivity).clearAllDrawn();
+ }
+
+ @Test
public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
win0.mActivityRecord.mVisibleRequested = false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d473c6a..541573c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14957,21 +14957,132 @@
* @return a Pair of (major version, minor version) or (-1,-1) if unknown.
*
* @hide
+ *
+ * @deprecated Use {@link #getHalVersion} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
@TestApi
public Pair<Integer, Integer> getRadioHalVersion() {
+ return getHalVersion(HAL_SERVICE_RADIO);
+ }
+
+ /** @hide */
+ public static final int HAL_SERVICE_RADIO = 0;
+
+ /**
+ * HAL service type that supports the HAL APIs implementation of IRadioData
+ * {@link RadioDataProxy}
+ * @hide
+ */
+ @TestApi
+ public static final int HAL_SERVICE_DATA = 1;
+
+ /**
+ * HAL service type that supports the HAL APIs implementation of IRadioMessaging
+ * {@link RadioMessagingProxy}
+ * @hide
+ */
+ @TestApi
+ public static final int HAL_SERVICE_MESSAGING = 2;
+
+ /**
+ * HAL service type that supports the HAL APIs implementation of IRadioModem
+ * {@link RadioModemProxy}
+ * @hide
+ */
+ @TestApi
+ public static final int HAL_SERVICE_MODEM = 3;
+
+ /**
+ * HAL service type that supports the HAL APIs implementation of IRadioNetwork
+ * {@link RadioNetworkProxy}
+ * @hide
+ */
+ @TestApi
+ public static final int HAL_SERVICE_NETWORK = 4;
+
+ /**
+ * HAL service type that supports the HAL APIs implementation of IRadioSim
+ * {@link RadioSimProxy}
+ * @hide
+ */
+ @TestApi
+ public static final int HAL_SERVICE_SIM = 5;
+
+ /**
+ * HAL service type that supports the HAL APIs implementation of IRadioVoice
+ * {@link RadioVoiceProxy}
+ * @hide
+ */
+ @TestApi
+ public static final int HAL_SERVICE_VOICE = 6;
+
+ /**
+ * HAL service type that supports the HAL APIs implementation of IRadioIms
+ * {@link RadioImsProxy}
+ * @hide
+ */
+ @TestApi
+ public static final int HAL_SERVICE_IMS = 7;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"HAL_SERVICE_"},
+ value = {
+ HAL_SERVICE_RADIO,
+ HAL_SERVICE_DATA,
+ HAL_SERVICE_MESSAGING,
+ HAL_SERVICE_MODEM,
+ HAL_SERVICE_NETWORK,
+ HAL_SERVICE_SIM,
+ HAL_SERVICE_VOICE,
+ HAL_SERVICE_IMS,
+ })
+ public @interface HalService {}
+
+ /**
+ * The HAL Version indicating that the version is unknown or invalid.
+ * @hide
+ */
+ @TestApi
+ public static final Pair HAL_VERSION_UNKNOWN = new Pair(-1, -1);
+
+ /**
+ * The HAL Version indicating that the version is unsupported.
+ * @hide
+ */
+ @TestApi
+ public static final Pair HAL_VERSION_UNSUPPORTED = new Pair(-2, -2);
+
+ /**
+ * Retrieve the HAL Version of a specific service for this device.
+ *
+ * Get the HAL version for a specific HAL interface for test purposes.
+ *
+ * @param halService the service id to query.
+ * @return a Pair of (major version, minor version), HAL_VERSION_UNKNOWN if unknown
+ * or HAL_VERSION_UNSUPPORTED if unsupported.
+ *
+ * @hide
+ */
+ @TestApi
+ public @NonNull Pair<Integer, Integer> getHalVersion(@HalService int halService) {
try {
ITelephony service = getITelephony();
if (service != null) {
- int version = service.getRadioHalVersion();
- if (version == -1) return new Pair<Integer, Integer>(-1, -1);
- return new Pair<Integer, Integer>(version / 100, version % 100);
+ int version = service.getHalVersion(halService);
+ if (version != -1) {
+ return new Pair<Integer, Integer>(version / 100, version % 100);
+ }
+ } else {
+ throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException e) {
- Log.e(TAG, "getRadioHalVersion() RemoteException", e);
+ Log.e(TAG, "getHalVersion() RemoteException", e);
+ e.rethrowAsRuntimeException();
}
- return new Pair<Integer, Integer>(-1, -1);
+ return HAL_VERSION_UNKNOWN;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 648866b..abf4cde 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2159,6 +2159,12 @@
int getRadioHalVersion();
/**
+ * Get the HAL Version of a specific service
+ * encoded as 100 * MAJOR_VERSION + MINOR_VERSION or -1 if unknown
+ */
+ int getHalVersion(int service);
+
+ /**
* Get the current calling package name.
*/
String getCurrentPackageName();