diff options
Diffstat (limited to 'libs')
41 files changed, 1277 insertions, 480 deletions
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 2825273333..20bf30122d 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -286,6 +286,9 @@ filegroup {          "aidl/android/content/pm/IPackageChangeObserver.aidl",          "aidl/android/content/pm/IPackageManagerNative.aidl",          "aidl/android/content/pm/PackageChangeEvent.aidl", +        "aidl/android/content/pm/IStagedApexObserver.aidl", +        "aidl/android/content/pm/ApexStagedEvent.aidl", +        "aidl/android/content/pm/StagedApexInfo.aidl",      ],      path: "aidl",  } diff --git a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl new file mode 100644 index 0000000000..75f87530f9 --- /dev/null +++ b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +/** + * This event is designed for notification to native code listener about + * any changes to set of apex packages staged for installation on next boot. + * + * @hide + */ +parcelable ApexStagedEvent { +  @utf8InCpp String[] stagedApexModuleNames; +} diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl index c20d9f6645..d71f496bf1 100644 --- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -18,6 +18,8 @@  package android.content.pm;  import android.content.pm.IPackageChangeObserver; +import android.content.pm.IStagedApexObserver; +import android.content.pm.StagedApexInfo;  /**   * Parallel implementation of certain {@link PackageManager} APIs that need to @@ -123,4 +125,24 @@ interface IPackageManagerNative {       * requested version.       */      boolean hasSystemFeature(in String featureName, in int version); + +    /** Register a observer for change in set of staged APEX ready for installation */ +    void registerStagedApexObserver(in IStagedApexObserver observer); + +    /** +     * Unregister an existing staged apex observer. +     * This does nothing if this observer was not already registered. +     */ +    void unregisterStagedApexObserver(in IStagedApexObserver observer); + +    /** +     * Get APEX module names of all APEX that are staged ready for installation +     */ +    @utf8InCpp String[] getStagedApexModuleNames(); + +    /** +     * Get information of APEX which is staged ready for installation. +     * Returns null if no such APEX is found. +     */ +    StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName);  } diff --git a/libs/ui/Size.cpp b/libs/binder/aidl/android/content/pm/IStagedApexObserver.aidl index d2996d164d..9906436acf 100644 --- a/libs/ui/Size.cpp +++ b/libs/binder/aidl/android/content/pm/IStagedApexObserver.aidl @@ -1,5 +1,5 @@  /* - * Copyright 2019 The Android Open Source Project + * Copyright (C) 2021 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. @@ -14,11 +14,15 @@   * limitations under the License.   */ -#include <ui/Size.h> +package android.content.pm; -namespace android::ui { +import android.content.pm.ApexStagedEvent; -const Size Size::INVALID{-1, -1}; -const Size Size::EMPTY{0, 0}; - -} // namespace android::ui +/** + * This is a non-blocking notification when set of staged apex has changed + * + * @hide + */ +oneway interface IStagedApexObserver { +  void onApexStaged(in ApexStagedEvent event); +} diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl new file mode 100644 index 0000000000..ece79895f7 --- /dev/null +++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +/** + * This object is designed for returning information regarding + * staged APEX that are ready to be installed on next reboot. + * + * @hide + */ +parcelable StagedApexInfo { +  @utf8InCpp String moduleName; +  @utf8InCpp String diskImagePath; +  long versionCode; +  @utf8InCpp String versionName; +} diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 2524c5f6d2..3026921044 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -14,12 +14,14 @@ cc_test {          address: true,      },      srcs: [ +        "cast_test.cpp",          "Flags_test.cpp",          "future_test.cpp",          "NamedEnum_test.cpp",          "small_map_test.cpp",          "small_vector_test.cpp",          "static_vector_test.cpp", +        "string_test.cpp",      ],      cflags: [          "-Wall", diff --git a/libs/ftl/cast_test.cpp b/libs/ftl/cast_test.cpp new file mode 100644 index 0000000000..2abcb8fe66 --- /dev/null +++ b/libs/ftl/cast_test.cpp @@ -0,0 +1,200 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/cast.h> +#include <gtest/gtest.h> + +#include <cfloat> +#include <cmath> +#include <limits> + +namespace android::test { + +using ftl::cast_safety; +using ftl::CastSafety; + +template <typename T> +constexpr T min = std::numeric_limits<T>::lowest(); + +template <typename T> +constexpr T max = std::numeric_limits<T>::max(); + +template <typename T> +constexpr T inf = std::numeric_limits<T>::infinity(); + +template <typename T> +constexpr T NaN = std::numeric_limits<T>::quiet_NaN(); + +// Keep in sync with example usage in header file. + +static_assert(cast_safety<uint8_t>(-1) == CastSafety::kUnderflow); +static_assert(cast_safety<int8_t>(128u) == CastSafety::kOverflow); + +static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow); +static_assert(cast_safety<int32_t>(static_cast<float>(INT32_MAX)) == CastSafety::kOverflow); + +static_assert(cast_safety<float>(-DBL_MAX) == CastSafety::kUnderflow); + +// Unsigned to unsigned. + +static_assert(cast_safety<uint8_t>(0u) == CastSafety::kSafe); +static_assert(cast_safety<uint16_t>(max<uint8_t>) == CastSafety::kSafe); +static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>)) == CastSafety::kSafe); + +static_assert(cast_safety<uint32_t>(max<uint64_t>) == CastSafety::kOverflow); +static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>) + 1) == +              CastSafety::kOverflow); + +// Unsigned to signed. + +static_assert(cast_safety<int16_t>(0u) == CastSafety::kSafe); +static_assert(cast_safety<int16_t>(max<uint8_t>) == CastSafety::kSafe); +static_assert(cast_safety<int16_t>(max<uint16_t>) == CastSafety::kOverflow); + +static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) - 1) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) + 1) == +              CastSafety::kOverflow); + +// Signed to unsigned. + +static_assert(cast_safety<uint16_t>(0) == CastSafety::kSafe); +static_assert(cast_safety<uint16_t>(max<int8_t>) == CastSafety::kSafe); +static_assert(cast_safety<uint16_t>(max<int16_t>) == CastSafety::kSafe); + +static_assert(cast_safety<uint32_t>(-1) == CastSafety::kUnderflow); +static_assert(cast_safety<uint32_t>(max<int64_t>) == CastSafety::kOverflow); + +static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) - 1) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) + 1) == +              CastSafety::kOverflow); + +// Signed to signed. + +static_assert(cast_safety<int8_t>(-129) == CastSafety::kUnderflow); +static_assert(cast_safety<int8_t>(-128) == CastSafety::kSafe); +static_assert(cast_safety<int8_t>(127) == CastSafety::kSafe); +static_assert(cast_safety<int8_t>(128) == CastSafety::kOverflow); + +static_assert(cast_safety<int32_t>(static_cast<int64_t>(min<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int32_t>(static_cast<int64_t>(max<int32_t>)) == CastSafety::kSafe); + +static_assert(cast_safety<int16_t>(min<int32_t>) == CastSafety::kUnderflow); +static_assert(cast_safety<int32_t>(max<int64_t>) == CastSafety::kOverflow); + +// Float to float. + +static_assert(cast_safety<double>(max<float>) == CastSafety::kSafe); +static_assert(cast_safety<double>(min<float>) == CastSafety::kSafe); + +static_assert(cast_safety<float>(min<double>) == CastSafety::kUnderflow); +static_assert(cast_safety<float>(max<double>) == CastSafety::kOverflow); + +TEST(CastSafety, FloatToFloat) { +  EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(min<float>), min<double>)), +            CastSafety::kUnderflow); +  EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(max<float>), max<double>)), +            CastSafety::kOverflow); +} + +// Unsigned to float. + +static_assert(cast_safety<float>(0u) == CastSafety::kSafe); +static_assert(cast_safety<float>(max<uint64_t>) == CastSafety::kSafe); + +static_assert(cast_safety<double>(0u) == CastSafety::kSafe); +static_assert(cast_safety<double>(max<uint64_t>) == CastSafety::kSafe); + +// Signed to float. + +static_assert(cast_safety<float>(min<int64_t>) == CastSafety::kSafe); +static_assert(cast_safety<float>(max<int64_t>) == CastSafety::kSafe); + +static_assert(cast_safety<double>(min<int64_t>) == CastSafety::kSafe); +static_assert(cast_safety<double>(max<int64_t>) == CastSafety::kSafe); + +// Float to unsigned. + +static_assert(cast_safety<uint32_t>(0.f) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(min<float>) == CastSafety::kUnderflow); +static_assert(cast_safety<uint32_t>(max<float>) == CastSafety::kOverflow); +static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow); + +static_assert(cast_safety<uint16_t>(-inf<float>) == CastSafety::kUnderflow); +static_assert(cast_safety<uint32_t>(inf<float>) == CastSafety::kOverflow); +static_assert(cast_safety<uint64_t>(NaN<float>) == CastSafety::kOverflow); + +static_assert(cast_safety<uint32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(static_cast<float>(max<uint32_t>)) == CastSafety::kOverflow); +static_assert(cast_safety<uint32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint32_t>(static_cast<double>(max<uint32_t>)) == CastSafety::kSafe); + +static_assert(cast_safety<uint64_t>(0.0) == CastSafety::kSafe); +static_assert(cast_safety<uint64_t>(min<double>) == CastSafety::kUnderflow); +static_assert(cast_safety<uint64_t>(max<double>) == CastSafety::kOverflow); +static_assert(cast_safety<uint64_t>(-.1) == CastSafety::kUnderflow); + +static_assert(cast_safety<uint64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint64_t>(static_cast<float>(max<uint64_t>)) == CastSafety::kOverflow); +static_assert(cast_safety<uint64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<uint64_t>(static_cast<double>(max<uint64_t>)) == CastSafety::kOverflow); + +// Float to signed. + +static_assert(cast_safety<int32_t>(0.f) == CastSafety::kSafe); +static_assert(cast_safety<int32_t>(min<float>) == CastSafety::kUnderflow); +static_assert(cast_safety<int32_t>(max<float>) == CastSafety::kOverflow); + +static_assert(cast_safety<int16_t>(-inf<double>) == CastSafety::kUnderflow); +static_assert(cast_safety<int32_t>(inf<double>) == CastSafety::kOverflow); +static_assert(cast_safety<int64_t>(NaN<double>) == CastSafety::kOverflow); + +static_assert(cast_safety<int32_t>(static_cast<float>(min<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kOverflow); +static_assert(cast_safety<int32_t>(static_cast<double>(min<int32_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe); + +static_assert(cast_safety<int64_t>(0.0) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(min<double>) == CastSafety::kUnderflow); +static_assert(cast_safety<int64_t>(max<double>) == CastSafety::kOverflow); + +static_assert(cast_safety<int64_t>(static_cast<float>(min<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kOverflow); +static_assert(cast_safety<int64_t>(static_cast<double>(min<int64_t>)) == CastSafety::kSafe); +static_assert(cast_safety<int64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kOverflow); + +TEST(CastSafety, FloatToSigned) { +  constexpr int32_t kMax = ftl::details::safe_limits<int32_t, float>::max(); +  static_assert(kMax == 2'147'483'520); +  EXPECT_EQ(kMax, static_cast<int32_t>(std::nexttowardf(max<int32_t>, 0))); + +  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, 0)), CastSafety::kSafe); +  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, 0)), CastSafety::kSafe); +  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, 0)), CastSafety::kSafe); +  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, 0)), CastSafety::kSafe); + +  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, min<float>)), +            CastSafety::kUnderflow); +  EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, max<float>)), +            CastSafety::kOverflow); +  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, min<double>)), +            CastSafety::kUnderflow); +  EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, max<double>)), +            CastSafety::kOverflow); +} + +}  // namespace android::test diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp index 323b9f91e7..2e81022f38 100644 --- a/libs/ftl/small_map_test.cpp +++ b/libs/ftl/small_map_test.cpp @@ -18,6 +18,9 @@  #include <gtest/gtest.h>  #include <cctype> +#include <string> + +using namespace std::string_literals;  namespace android::test { @@ -35,16 +38,19 @@ TEST(SmallMap, Example) {    EXPECT_TRUE(map.contains(123)); -  EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u); +  EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u); -  const auto opt = map.find(-1); +  const auto opt = map.get(-1);    ASSERT_TRUE(opt);    std::string& ref = *opt;    EXPECT_TRUE(ref.empty());    ref = "xyz"; -  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc"))); +  map.emplace_or_replace(0, "vanilla", 2u, 3u); +  EXPECT_TRUE(map.dynamic()); + +  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));  }  TEST(SmallMap, Construct) { @@ -90,42 +96,253 @@ TEST(SmallMap, Construct) {    }  } +TEST(SmallMap, UniqueKeys) { +  { +    // Duplicate mappings are discarded. +    const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1); + +    EXPECT_EQ(map.size(), 3u); +    EXPECT_EQ(map.max_size(), 9u); + +    using Map = decltype(map); +    EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f))); +  } +  { +    // Duplicate mappings may be reordered. +    const SmallMap map = ftl::init::map('a', 'A')( +        'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F'); + +    EXPECT_EQ(map.size(), 6u); +    EXPECT_EQ(map.max_size(), 12u); + +    using Map = decltype(map); +    EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F'))); +  } +} +  TEST(SmallMap, Find) {    {      // Constant reference. -    const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); +    const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); -    const auto opt = map.find('b'); +    const auto opt = map.get('b');      EXPECT_EQ(opt, 'B');      const char d = 'D'; -    const auto ref = map.find('d').value_or(std::cref(d)); +    const auto ref = map.get('d').value_or(std::cref(d));      EXPECT_EQ(ref.get(), 'D');    }    {      // Mutable reference. -    ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); +    SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); -    const auto opt = map.find('c'); +    const auto opt = map.get('c');      EXPECT_EQ(opt, 'C');      char d = 'd'; -    const auto ref = map.find('d').value_or(std::ref(d)); +    const auto ref = map.get('d').value_or(std::ref(d));      ref.get() = 'D';      EXPECT_EQ(d, 'D');    }    {      // Constant unary operation. -    const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); -    EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z'); +    const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); +    EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z');    }    {      // Mutable unary operation. -    ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); -    EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); })); +    SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z'); +    EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); }));      EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));    }  } +TEST(SmallMap, TryEmplace) { +  SmallMap<int, std::string, 3> map; +  using Pair = decltype(map)::value_type; + +  { +    const auto [it, ok] = map.try_emplace(123, "abc"); +    ASSERT_TRUE(ok); +    EXPECT_EQ(*it, Pair(123, "abc"s)); +  } +  { +    const auto [it, ok] = map.try_emplace(42, 3u, '?'); +    ASSERT_TRUE(ok); +    EXPECT_EQ(*it, Pair(42, "???"s)); +  } +  { +    const auto [it, ok] = map.try_emplace(-1); +    ASSERT_TRUE(ok); +    EXPECT_EQ(*it, Pair(-1, std::string())); +    EXPECT_FALSE(map.dynamic()); +  } +  { +    // Insertion fails if mapping exists. +    const auto [it, ok] = map.try_emplace(42, "!!!"); +    EXPECT_FALSE(ok); +    EXPECT_EQ(*it, Pair(42, "???")); +    EXPECT_FALSE(map.dynamic()); +  } +  { +    // Insertion at capacity promotes the map. +    const auto [it, ok] = map.try_emplace(999, "xyz"); +    ASSERT_TRUE(ok); +    EXPECT_EQ(*it, Pair(999, "xyz")); +    EXPECT_TRUE(map.dynamic()); +  } + +  EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s))); +} + +namespace { + +// The mapped type does not require a copy/move assignment operator. +struct String { +  template <typename... Args> +  String(Args... args) : str(args...) {} +  const std::string str; + +  bool operator==(const String& other) const { return other.str == str; } +}; + +}  // namespace + +TEST(SmallMap, TryReplace) { +  SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); +  using Pair = decltype(map)::value_type; + +  { +    // Replacing fails unless mapping exists. +    const auto it = map.try_replace(3, "c"); +    EXPECT_EQ(it, map.end()); +  } +  { +    // Replacement arguments can refer to the replaced mapping. +    const auto ref = map.get(2, [](const auto& s) { return s.str[0]; }); +    ASSERT_TRUE(ref); + +    // Construct std::string from one character. +    const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref))); +    ASSERT_NE(it, map.end()); +    EXPECT_EQ(*it, Pair(2, "b")); +  } + +  EXPECT_FALSE(map.dynamic()); +  EXPECT_TRUE(map.try_emplace(3, "abc").second); +  EXPECT_TRUE(map.try_emplace(4, "d").second); +  EXPECT_TRUE(map.dynamic()); + +  { +    // Replacing fails unless mapping exists. +    const auto it = map.try_replace(5, "e"); +    EXPECT_EQ(it, map.end()); +  } +  { +    // Replacement arguments can refer to the replaced mapping. +    const auto ref = map.get(3); +    ASSERT_TRUE(ref); + +    // Construct std::string from substring. +    const auto it = map.try_replace(3, ref->get().str, 2u, 1u); +    ASSERT_NE(it, map.end()); +    EXPECT_EQ(*it, Pair(3, "c")); +  } + +  EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); +} + +TEST(SmallMap, EmplaceOrReplace) { +  SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); +  using Pair = decltype(map)::value_type; + +  { +    // New mapping is emplaced. +    const auto [it, emplace] = map.emplace_or_replace(3, "c"); +    EXPECT_TRUE(emplace); +    EXPECT_EQ(*it, Pair(3, "c")); +  } +  { +    // Replacement arguments can refer to the replaced mapping. +    const auto ref = map.get(2, [](const auto& s) { return s.str[0]; }); +    ASSERT_TRUE(ref); + +    // Construct std::string from one character. +    const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref))); +    EXPECT_FALSE(emplace); +    EXPECT_EQ(*it, Pair(2, "b")); +  } + +  EXPECT_FALSE(map.dynamic()); +  EXPECT_FALSE(map.emplace_or_replace(3, "abc").second);  // Replace. +  EXPECT_TRUE(map.emplace_or_replace(4, "d").second);     // Emplace. +  EXPECT_TRUE(map.dynamic()); + +  { +    // New mapping is emplaced. +    const auto [it, emplace] = map.emplace_or_replace(5, "e"); +    EXPECT_TRUE(emplace); +    EXPECT_EQ(*it, Pair(5, "e")); +  } +  { +    // Replacement arguments can refer to the replaced mapping. +    const auto ref = map.get(3); +    ASSERT_TRUE(ref); + +    // Construct std::string from substring. +    const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u); +    EXPECT_FALSE(emplace); +    EXPECT_EQ(*it, Pair(3, "c")); +  } + +  EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); +} + +TEST(SmallMap, Erase) { +  { +    SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4'); +    EXPECT_FALSE(map.dynamic()); + +    EXPECT_FALSE(map.erase(0));  // Key not found. + +    EXPECT_TRUE(map.erase(2)); +    EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4'))); + +    EXPECT_TRUE(map.erase(1)); +    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4'))); + +    EXPECT_TRUE(map.erase(4)); +    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3'))); + +    EXPECT_TRUE(map.erase(3)); +    EXPECT_FALSE(map.erase(3));  // Key not found. + +    EXPECT_TRUE(map.empty()); +    EXPECT_FALSE(map.dynamic()); +  } +  { +    SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3'); +    map.try_emplace(4, '4'); +    EXPECT_TRUE(map.dynamic()); + +    EXPECT_FALSE(map.erase(0));  // Key not found. + +    EXPECT_TRUE(map.erase(2)); +    EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4'))); + +    EXPECT_TRUE(map.erase(1)); +    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4'))); + +    EXPECT_TRUE(map.erase(4)); +    EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3'))); + +    EXPECT_TRUE(map.erase(3)); +    EXPECT_FALSE(map.erase(3));  // Key not found. + +    EXPECT_TRUE(map.empty()); +    EXPECT_TRUE(map.dynamic()); +  } +} +  }  // namespace android::test diff --git a/libs/ftl/string_test.cpp b/libs/ftl/string_test.cpp new file mode 100644 index 0000000000..f3d85c8319 --- /dev/null +++ b/libs/ftl/string_test.cpp @@ -0,0 +1,187 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/string.h> +#include <gtest/gtest.h> + +#include <algorithm> +#include <cstdint> +#include <iterator> +#include <limits> +#include <sstream> +#include <type_traits> + +namespace android::test { + +// Keep in sync with example usage in header file. +TEST(String, ToChars) { +  ftl::to_chars_buffer_t<> buffer; + +  EXPECT_EQ(ftl::to_chars(buffer, 123u), "123"); +  EXPECT_EQ(ftl::to_chars(buffer, -42, ftl::Radix::kBin), "-0b101010"); +  EXPECT_EQ(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex), "0xcafe"); +  EXPECT_EQ(ftl::to_chars(buffer, '*', ftl::Radix::kHex), "0x2a"); +} + +namespace { + +template <typename F, typename T> +void ToCharsTest() { +  constexpr auto kRadix = F::kRadix; + +  using Limits = std::numeric_limits<T>; +  constexpr auto kMin = Limits::min(); +  constexpr auto kMax = Limits::max(); +  constexpr auto kNeg = static_cast<T>(-42); +  constexpr auto kPos = static_cast<T>(123); + +  ftl::to_chars_buffer_t<T> buffer; + +  EXPECT_EQ(ftl::to_chars(buffer, kMin, kRadix), F{}(kMin)); +  EXPECT_EQ(ftl::to_chars(buffer, kMax, kRadix), F{}(kMax)); +  EXPECT_EQ(ftl::to_chars(buffer, kNeg, kRadix), F{}(kNeg)); +  EXPECT_EQ(ftl::to_chars(buffer, kPos, kRadix), F{}(kPos)); +} + +template <typename...> +struct Types {}; + +template <typename F, typename Types> +struct ToCharsTests; + +template <typename F, typename T, typename... Ts> +struct ToCharsTests<F, Types<T, Ts...>> { +  static void test() { +    ToCharsTest<F, T>(); +    ToCharsTests<F, Types<Ts...>>::test(); +  } +}; + +template <typename F> +struct ToCharsTests<F, Types<>> { +  static void test() {} +}; + +template <typename T, typename U = std::make_unsigned_t<T>> +U to_unsigned(std::ostream& stream, T v) { +  if (std::is_same_v<T, U>) return v; + +  if (v < 0) { +    stream << '-'; +    return std::numeric_limits<U>::max() - static_cast<U>(v) + 1; +  } else { +    return static_cast<U>(v); +  } +} + +struct Bin { +  static constexpr auto kRadix = ftl::Radix::kBin; + +  template <typename T> +  std::string operator()(T v) const { +    std::ostringstream stream; +    auto u = to_unsigned(stream, v); +    stream << "0b"; + +    if (u == 0) { +      stream << 0; +    } else { +      std::ostringstream digits; +      do { +        digits << (u & 1); +      } while (u >>= 1); + +      const auto str = digits.str(); +      std::copy(str.rbegin(), str.rend(), std::ostream_iterator<char>(stream)); +    } + +    return stream.str(); +  } +}; + +struct Dec { +  static constexpr auto kRadix = ftl::Radix::kDec; + +  template <typename T> +  std::string operator()(T v) const { +    return std::to_string(v); +  } +}; + +struct Hex { +  static constexpr auto kRadix = ftl::Radix::kHex; + +  template <typename T> +  std::string operator()(T v) const { +    std::ostringstream stream; +    const auto u = to_unsigned(stream, v); +    stream << "0x" << std::hex << std::nouppercase; +    stream << (sizeof(T) == 1 ? static_cast<unsigned>(u) : u); +    return stream.str(); +  } +}; + +using IntegerTypes = +    Types<char, unsigned char, signed char, std::uint8_t, std::uint16_t, std::uint32_t, +          std::uint64_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t>; + +}  // namespace + +TEST(String, ToCharsBin) { +  ToCharsTests<Bin, IntegerTypes>::test(); + +  { +    const std::uint8_t x = 0b1111'1111; +    ftl::to_chars_buffer_t<decltype(x)> buffer; +    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "0b11111111"); +  } +  { +    const std::int16_t x = -0b1000'0000'0000'0000; +    ftl::to_chars_buffer_t<decltype(x)> buffer; +    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "-0b1000000000000000"); +  } +} + +TEST(String, ToCharsDec) { +  ToCharsTests<Dec, IntegerTypes>::test(); + +  { +    const std::uint32_t x = UINT32_MAX; +    ftl::to_chars_buffer_t<decltype(x)> buffer; +    EXPECT_EQ(ftl::to_chars(buffer, x), "4294967295"); +  } +  { +    const std::int32_t x = INT32_MIN; +    ftl::to_chars_buffer_t<decltype(x)> buffer; +    EXPECT_EQ(ftl::to_chars(buffer, x), "-2147483648"); +  } +} + +TEST(String, ToCharsHex) { +  ToCharsTests<Hex, IntegerTypes>::test(); + +  { +    const std::uint16_t x = 0xfade; +    ftl::to_chars_buffer_t<decltype(x)> buffer; +    EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kHex), "0xfade"); +  } +  { +    ftl::to_chars_buffer_t<> buffer; +    EXPECT_EQ(ftl::to_chars(buffer, INT64_MIN, ftl::Radix::kHex), "-0x8000000000000000"); +  } +} + +}  // namespace android::test diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index d54de4999c..7f0cac5d4f 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -343,80 +343,6 @@ void* GraphicsEnv::loadLibrary(std::string name) {      return nullptr;  } -bool GraphicsEnv::checkAngleRules(void* so) { -    auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET"); -    auto model = base::GetProperty("ro.product.model", "UNSET"); - -    auto ANGLEGetFeatureSupportUtilAPIVersion = -            (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, -                                                          "ANGLEGetFeatureSupportUtilAPIVersion"); - -    if (!ANGLEGetFeatureSupportUtilAPIVersion) { -        ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function"); -        return false; -    } - -    // Negotiate the interface version by requesting most recent known to the platform -    unsigned int versionToUse = CURRENT_ANGLE_API_VERSION; -    if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) { -        ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, " -              "requested version %u", -              versionToUse); -        return false; -    } - -    // Add and remove versions below as needed -    bool useAngle = false; -    switch (versionToUse) { -        case 2: { -            ALOGV("Using version %d of ANGLE feature-support library", versionToUse); -            void* rulesHandle = nullptr; -            int rulesVersion = 0; -            void* systemInfoHandle = nullptr; - -            // Get the symbols for the feature-support-utility library: -#define GET_SYMBOL(symbol)                                                 \ -    fp##symbol symbol = (fp##symbol)dlsym(so, #symbol);                    \ -    if (!symbol) {                                                         \ -        ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \ -        break;                                                             \ -    } -            GET_SYMBOL(ANGLEAndroidParseRulesString); -            GET_SYMBOL(ANGLEGetSystemInfo); -            GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo); -            GET_SYMBOL(ANGLEShouldBeUsedForApplication); -            GET_SYMBOL(ANGLEFreeRulesHandle); -            GET_SYMBOL(ANGLEFreeSystemInfoHandle); - -            // Parse the rules, obtain the SystemInfo, and evaluate the -            // application against the rules: -            if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) { -                ALOGW("ANGLE feature-support library cannot parse rules file"); -                break; -            } -            if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) { -                ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); -                break; -            } -            if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(), -                                                  systemInfoHandle)) { -                ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); -                break; -            } -            useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion, -                                                         systemInfoHandle, mAngleAppName.c_str()); -            (ANGLEFreeRulesHandle)(rulesHandle); -            (ANGLEFreeSystemInfoHandle)(systemInfoHandle); -        } break; - -        default: -            ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse); -    } - -    ALOGV("Close temporarily-loaded ANGLE opt-in/out logic"); -    return useAngle; -} -  bool GraphicsEnv::shouldUseAngle(std::string appName) {      if (appName != mAngleAppName) {          // Make sure we are checking the app we were init'ed for @@ -444,31 +370,20 @@ void GraphicsEnv::updateUseAngle() {      const char* ANGLE_PREFER_ANGLE = "angle";      const char* ANGLE_PREFER_NATIVE = "native"; +    mUseAngle = NO;      if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {          ALOGV("User set \"Developer Options\" to force the use of ANGLE");          mUseAngle = YES;      } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {          ALOGV("User set \"Developer Options\" to force the use of Native"); -        mUseAngle = NO;      } else { -        // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily -        // load ANGLE and call the updatable opt-in/out logic: -        void* featureSo = loadLibrary("feature_support"); -        if (featureSo) { -            ALOGV("loaded ANGLE's opt-in/out logic from namespace"); -            mUseAngle = checkAngleRules(featureSo) ? YES : NO; -            dlclose(featureSo); -            featureSo = nullptr; -        } else { -            ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE."); -        } +        ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());      }  }  void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,                                 const std::string developerOptIn, -                               const std::vector<std::string> eglFeatures, const int rulesFd, -                               const long rulesOffset, const long rulesLength) { +                               const std::vector<std::string> eglFeatures) {      if (mUseAngle != UNKNOWN) {          // We've already figured out an answer for this app, so just return.          ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(), @@ -485,22 +400,6 @@ void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName      ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());      mAngleDeveloperOptIn = developerOptIn; -    lseek(rulesFd, rulesOffset, SEEK_SET); -    mRulesBuffer = std::vector<char>(rulesLength + 1); -    ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength); -    if (numBytesRead < 0) { -        ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead); -        numBytesRead = 0; -    } else if (numBytesRead == 0) { -        ALOGW("Empty rules file"); -    } -    if (numBytesRead != rulesLength) { -        ALOGW("Did not read all of the necessary bytes from the rules file." -              "expected: %ld, got: %zd", -              rulesLength, numBytesRead); -    } -    mRulesBuffer[numBytesRead] = '\0'; -      // Update the current status of whether we should use ANGLE or not      updateUseAngle();  } diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 900fc49b59..56d1139f57 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -97,8 +97,7 @@ public:      // in the search path must have a '!' after the zip filename, e.g.      //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a      void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, -                      const std::vector<std::string> eglFeatures, const int rulesFd, -                      const long rulesOffset, const long rulesLength); +                      const std::vector<std::string> eglFeatures);      // Get the ANGLE driver namespace.      android_namespace_t* getAngleNamespace();      // Get the app name for ANGLE debug message. @@ -129,8 +128,6 @@ private:      // Load requested ANGLE library.      void* loadLibrary(std::string name); -    // Check ANGLE support with the rules. -    bool checkAngleRules(void* so);      // Update whether ANGLE should be used.      void updateUseAngle();      // Link updatable driver namespace with llndk and vndk-sp libs. @@ -159,8 +156,6 @@ private:      std::string mAngleDeveloperOptIn;      // ANGLE EGL features;      std::vector<std::string> mAngleEglFeatures; -    // ANGLE rules. -    std::vector<char> mRulesBuffer;      // Use ANGLE flag.      UseAngle mUseAngle = UNKNOWN;      // Vulkan debug layers libs. diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 2a980bd118..3bf63062f6 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -124,11 +124,11 @@ public:          return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);      } -    status_t captureDisplay(uint64_t displayOrLayerStack, +    status_t captureDisplay(DisplayId displayId,                              const sp<IScreenCaptureListener>& captureListener) override {          Parcel data, reply;          data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); -        SAFE_PARCEL(data.writeUint64, displayOrLayerStack); +        SAFE_PARCEL(data.writeUint64, displayId.value);          SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));          return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply); @@ -282,9 +282,14 @@ public:              NO_ERROR) {              std::vector<uint64_t> rawIds;              if (reply.readUint64Vector(&rawIds) == NO_ERROR) { -                std::vector<PhysicalDisplayId> displayIds(rawIds.size()); -                std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(), -                               [](uint64_t rawId) { return PhysicalDisplayId(rawId); }); +                std::vector<PhysicalDisplayId> displayIds; +                displayIds.reserve(rawIds.size()); + +                for (const uint64_t rawId : rawIds) { +                    if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) { +                        displayIds.push_back(*id); +                    } +                }                  return displayIds;              }          } @@ -1344,12 +1349,15 @@ status_t BnSurfaceComposer::onTransact(          }          case CAPTURE_DISPLAY_BY_ID: {              CHECK_INTERFACE(ISurfaceComposer, data, reply); -            uint64_t displayOrLayerStack = 0; +            uint64_t value; +            SAFE_PARCEL(data.readUint64, &value); +            const auto id = DisplayId::fromValue(value); +            if (!id) return BAD_VALUE; +              sp<IScreenCaptureListener> captureListener; -            SAFE_PARCEL(data.readUint64, &displayOrLayerStack);              SAFE_PARCEL(data.readStrongBinder, &captureListener); -            return captureDisplay(displayOrLayerStack, captureListener); +            return captureDisplay(*id, captureListener);          }          case CAPTURE_LAYERS: {              CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1416,9 +1424,9 @@ status_t BnSurfaceComposer::onTransact(          }          case GET_PHYSICAL_DISPLAY_TOKEN: {              CHECK_INTERFACE(ISurfaceComposer, data, reply); -            PhysicalDisplayId displayId(data.readUint64()); -            sp<IBinder> display = getPhysicalDisplayToken(displayId); -            reply->writeStrongBinder(display); +            const auto id = DisplayId::fromValue<PhysicalDisplayId>(data.readUint64()); +            if (!id) return BAD_VALUE; +            reply->writeStrongBinder(getPhysicalDisplayToken(*id));              return NO_ERROR;          }          case GET_DISPLAY_STATE: { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 8a7a8711bf..1fd9d13902 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -41,7 +41,6 @@ layer_state_t::layer_state_t()          z(0),          w(0),          h(0), -        layerStack(0),          alpha(0),          flags(0),          mask(0), @@ -86,7 +85,7 @@ status_t layer_state_t::write(Parcel& output) const      SAFE_PARCEL(output.writeInt32, z);      SAFE_PARCEL(output.writeUint32, w);      SAFE_PARCEL(output.writeUint32, h); -    SAFE_PARCEL(output.writeUint32, layerStack); +    SAFE_PARCEL(output.writeUint32, layerStack.id);      SAFE_PARCEL(output.writeFloat, alpha);      SAFE_PARCEL(output.writeUint32, flags);      SAFE_PARCEL(output.writeUint32, mask); @@ -188,7 +187,7 @@ status_t layer_state_t::read(const Parcel& input)      SAFE_PARCEL(input.readInt32, &z);      SAFE_PARCEL(input.readUint32, &w);      SAFE_PARCEL(input.readUint32, &h); -    SAFE_PARCEL(input.readUint32, &layerStack); +    SAFE_PARCEL(input.readUint32, &layerStack.id);      SAFE_PARCEL(input.readFloat, &alpha);      SAFE_PARCEL(input.readUint32, &flags); @@ -316,21 +315,14 @@ status_t ComposerState::read(const Parcel& input) {      return state.read(input);  } -DisplayState::DisplayState() -      : what(0), -        layerStack(0), -        flags(0), -        layerStackSpaceRect(Rect::EMPTY_RECT), -        orientedDisplaySpaceRect(Rect::EMPTY_RECT), -        width(0), -        height(0) {} +DisplayState::DisplayState() = default;  status_t DisplayState::write(Parcel& output) const {      SAFE_PARCEL(output.writeStrongBinder, token);      SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));      SAFE_PARCEL(output.writeUint32, what); -    SAFE_PARCEL(output.writeUint32, layerStack);      SAFE_PARCEL(output.writeUint32, flags); +    SAFE_PARCEL(output.writeUint32, layerStack.id);      SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));      SAFE_PARCEL(output.write, layerStackSpaceRect);      SAFE_PARCEL(output.write, orientedDisplaySpaceRect); @@ -346,8 +338,8 @@ status_t DisplayState::read(const Parcel& input) {      surface = interface_cast<IGraphicBufferProducer>(tmpBinder);      SAFE_PARCEL(input.readUint32, &what); -    SAFE_PARCEL(input.readUint32, &layerStack);      SAFE_PARCEL(input.readUint32, &flags); +    SAFE_PARCEL(input.readUint32, &layerStack.id);      uint32_t tmpUint = 0;      SAFE_PARCEL(input.readUint32, &tmpUint);      orientation = ui::toRotation(tmpUint); @@ -563,10 +555,13 @@ void layer_state_t::merge(const layer_state_t& other) {          what |= eDestinationFrameChanged;          destinationFrame = other.destinationFrame;      } +    if (other.what & eProducerDisconnect) { +        what |= eProducerDisconnect; +    }      if ((other.what & what) != other.what) {          ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " -              "other.what=0x%" PRIu64 " what=0x%" PRIu64, -              other.what, what); +              "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64, +              other.what, what, (other.what & what) ^ other.what);      }  } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index dc71b6a212..05554cab94 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1095,7 +1095,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha  }  SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayerStack( -        const sp<SurfaceControl>& sc, uint32_t layerStack) { +        const sp<SurfaceControl>& sc, ui::LayerStack layerStack) {      layer_state_t* s = getLayerState(sc);      if (!s) {          mStatus = BAD_INDEX; @@ -1760,7 +1760,7 @@ status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>  }  void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& token, -        uint32_t layerStack) { +                                                              ui::LayerStack layerStack) {      DisplayState& s(getDisplayState(token));      s.layerStack = layerStack;      s.what |= DisplayState::eLayerStackChanged; @@ -2184,12 +2184,12 @@ status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,      return s->captureDisplay(captureArgs, captureListener);  } -status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack, +status_t ScreenshotClient::captureDisplay(DisplayId displayId,                                            const sp<IScreenCaptureListener>& captureListener) {      sp<ISurfaceComposer> s(ComposerService::getComposerService());      if (s == nullptr) return NO_INIT; -    return s->captureDisplay(displayOrLayerStack, captureListener); +    return s->captureDisplay(displayId, captureListener);  }  status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index ad7bcb7d12..b7cd082da0 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -116,6 +116,11 @@ public:      using EventRegistrationFlags = Flags<EventRegistration>; +    template <typename T> +    struct SpHash { +        size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); } +    }; +      /*       * Create a connection with SurfaceFlinger.       */ @@ -239,24 +244,17 @@ public:       * The subregion can be optionally rotated.  It will also be scaled to       * match the size of the output buffer.       */ -    virtual status_t captureDisplay(const DisplayCaptureArgs& args, -                                    const sp<IScreenCaptureListener>& captureListener) = 0; +    virtual status_t captureDisplay(const DisplayCaptureArgs&, +                                    const sp<IScreenCaptureListener>&) = 0; -    virtual status_t captureDisplay(uint64_t displayOrLayerStack, -                                    const sp<IScreenCaptureListener>& captureListener) = 0; - -    template <class AA> -    struct SpHash { -        size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); } -    }; +    virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0;      /**       * Capture a subtree of the layer hierarchy, potentially ignoring the root node.       * This requires READ_FRAME_BUFFER permission. This function will fail if there       * is a secure window on screen       */ -    virtual status_t captureLayers(const LayerCaptureArgs& args, -                                   const sp<IScreenCaptureListener>& captureListener) = 0; +    virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0;      /* Clears the frame statistics for animations.       * diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 8dc8b9a87b..f14127c9de 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -35,6 +35,7 @@  #include <math/vec3.h>  #include <ui/BlurRegion.h>  #include <ui/GraphicTypes.h> +#include <ui/LayerStack.h>  #include <ui/Rect.h>  #include <ui/Region.h>  #include <ui/Rotation.h> @@ -143,7 +144,7 @@ struct layer_state_t {      int32_t z;      uint32_t w;      uint32_t h; -    uint32_t layerStack; +    ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;      float alpha;      uint32_t flags;      uint32_t mask; @@ -267,11 +268,12 @@ struct DisplayState {      DisplayState();      void merge(const DisplayState& other); -    uint32_t what; +    uint32_t what = 0; +    uint32_t flags = 0;      sp<IBinder> token;      sp<IGraphicBufferProducer> surface; -    uint32_t layerStack; -    uint32_t flags; + +    ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;      // These states define how layers are projected onto the physical display.      // @@ -285,10 +287,11 @@ struct DisplayState {      // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,      // 0).      ui::Rotation orientation = ui::ROTATION_0; -    Rect layerStackSpaceRect; -    Rect orientedDisplaySpaceRect; +    Rect layerStackSpaceRect = Rect::EMPTY_RECT; +    Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT; -    uint32_t width, height; +    uint32_t width = 0; +    uint32_t height = 0;      status_t write(Parcel& output) const;      status_t read(const Parcel& input); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 1d758e0dde..a980ce24ca 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -456,7 +456,7 @@ public:                                               int backgroundBlurRadius);          Transaction& setBlurRegions(const sp<SurfaceControl>& sc,                                      const std::vector<BlurRegion>& regions); -        Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); +        Transaction& setLayerStack(const sp<SurfaceControl>&, ui::LayerStack);          Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);          /// Reparents the current layer to the new parent handle. The new parent must not be null. @@ -566,7 +566,7 @@ public:          status_t setDisplaySurface(const sp<IBinder>& token,                  const sp<IGraphicBufferProducer>& bufferProducer); -        void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack); +        void setDisplayLayerStack(const sp<IBinder>& token, ui::LayerStack);          void setDisplayFlags(const sp<IBinder>& token, uint32_t flags); @@ -638,12 +638,9 @@ private:  class ScreenshotClient {  public: -    static status_t captureDisplay(const DisplayCaptureArgs& captureArgs, -                                   const sp<IScreenCaptureListener>& captureListener); -    static status_t captureDisplay(uint64_t displayOrLayerStack, -                                   const sp<IScreenCaptureListener>& captureListener); -    static status_t captureLayers(const LayerCaptureArgs& captureArgs, -                                  const sp<IScreenCaptureListener>& captureListener); +    static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); +    static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&); +    static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);  };  // --------------------------------------------------------------------------- diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 9082d275a2..26d902d849 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -128,7 +128,7 @@ protected:          mDisplayToken = mClient->getInternalDisplayToken();          ASSERT_NE(nullptr, mDisplayToken.get());          Transaction t; -        t.setDisplayLayerStack(mDisplayToken, 0); +        t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);          t.apply();          t.clear(); @@ -142,7 +142,7 @@ protected:                                                   mDisplayHeight, PIXEL_FORMAT_RGBA_8888,                                                   ISurfaceComposerClient::eFXSurfaceBufferState,                                                   /*parent*/ nullptr); -        t.setLayerStack(mSurfaceControl, 0) +        t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)                  .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())                  .show(mSurfaceControl)                  .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB) @@ -490,7 +490,7 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {                                       ISurfaceComposerClient::eFXSurfaceEffect);      ASSERT_NE(nullptr, bg.get());      Transaction t; -    t.setLayerStack(bg, 0) +    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)              .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))              .setColor(bg, half3{0, 0, 0})              .setLayer(bg, 0) @@ -545,7 +545,7 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {                                       ISurfaceComposerClient::eFXSurfaceEffect);      ASSERT_NE(nullptr, bg.get());      Transaction t; -    t.setLayerStack(bg, 0) +    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)              .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))              .setColor(bg, half3{0, 0, 0})              .setLayer(bg, 0) @@ -612,7 +612,7 @@ TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {                                       ISurfaceComposerClient::eFXSurfaceEffect);      ASSERT_NE(nullptr, bg.get());      Transaction t; -    t.setLayerStack(bg, 0) +    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)              .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))              .setColor(bg, half3{0, 0, 0})              .setLayer(bg, 0) @@ -733,7 +733,7 @@ TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) {                                     ISurfaceComposerClient::eFXSurfaceBufferState);      ASSERT_NE(nullptr, bgSurface.get());      Transaction t; -    t.setLayerStack(bgSurface, 0) +    t.setLayerStack(bgSurface, ui::DEFAULT_LAYER_STACK)              .show(bgSurface)              .setDataspace(bgSurface, ui::Dataspace::V0_SRGB)              .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1) diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index a02970c9bc..d1ad478dd1 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -752,21 +752,19 @@ public:      }      status_t setActiveColorMode(const sp<IBinder>& /*display*/,          ColorMode /*colorMode*/) override { return NO_ERROR; } -    status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */, -                            const sp<IScreenCaptureListener>& /* captureListener */) override { -        return NO_ERROR; -    }      void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}      void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {} -    status_t captureDisplay(uint64_t /*displayOrLayerStack*/, -                            const sp<IScreenCaptureListener>& /* captureListener */) override { + +    status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override {          return NO_ERROR;      } -    virtual status_t captureLayers( -            const LayerCaptureArgs& /* captureArgs */, -            const sp<IScreenCaptureListener>& /* captureListener */) override { +    status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override {          return NO_ERROR;      } +    status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override { +        return NO_ERROR; +    } +      status_t clearAnimationFrameStats() override { return NO_ERROR; }      status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {          return NO_ERROR; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 35209f7a07..1e8ff945ef 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -170,6 +170,9 @@ const char* inputEventTypeToString(int32_t type) {          case AINPUT_EVENT_TYPE_DRAG: {              return "DRAG";          } +        case AINPUT_EVENT_TYPE_TOUCH_MODE: { +            return "TOUCH_MODE"; +        }      }      return "UNKNOWN";  } @@ -330,10 +333,6 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win      scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale);  } -void PointerCoords::scale(float globalScaleFactor) { -    scale(globalScaleFactor, globalScaleFactor, globalScaleFactor); -} -  void PointerCoords::applyOffset(float xOffset, float yOffset) {      setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset);      setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); @@ -899,6 +898,19 @@ void DragEvent::initialize(const DragEvent& from) {      mY = from.mY;  } +// --- TouchModeEvent --- + +void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) { +    InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, +                           ADISPLAY_ID_NONE, INVALID_HMAC); +    mIsInTouchMode = isInTouchMode; +} + +void TouchModeEvent::initialize(const TouchModeEvent& from) { +    InputEvent::initialize(from); +    mIsInTouchMode = from.mIsInTouchMode; +} +  // --- PooledInputEventFactory ---  PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : @@ -953,6 +965,15 @@ DragEvent* PooledInputEventFactory::createDragEvent() {      return event;  } +TouchModeEvent* PooledInputEventFactory::createTouchModeEvent() { +    if (mTouchModeEventPool.empty()) { +        return new TouchModeEvent(); +    } +    TouchModeEvent* event = mTouchModeEventPool.front().release(); +    mTouchModeEventPool.pop(); +    return event; +} +  void PooledInputEventFactory::recycle(InputEvent* event) {      switch (event->getType()) {      case AINPUT_EVENT_TYPE_KEY: diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index ea8b9a7ec8..1e93dfb488 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -116,6 +116,7 @@ bool InputMessage::isValid(size_t actualSize) const {          case Type::FOCUS:          case Type::CAPTURE:          case Type::DRAG: +        case Type::TOUCH_MODE:              return true;          case Type::TIMELINE: {              const nsecs_t gpuCompletedTime = @@ -151,6 +152,8 @@ size_t InputMessage::size() const {              return sizeof(Header) + body.drag.size();          case Type::TIMELINE:              return sizeof(Header) + body.timeline.size(); +        case Type::TOUCH_MODE: +            return sizeof(Header) + body.touchMode.size();      }      return sizeof(Header);  } @@ -293,6 +296,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const {              msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;              break;          } +        case InputMessage::Type::TOUCH_MODE: { +            msg->body.touchMode.eventId = body.touchMode.eventId; +            msg->body.touchMode.isInTouchMode = body.touchMode.isInTouchMode; +        }      }  } @@ -665,6 +672,22 @@ status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x      return mChannel->sendMessage(&msg);  } +status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode) { +    if (ATRACE_ENABLED()) { +        std::string message = +                StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)", +                             mChannel->getName().c_str(), toString(isInTouchMode)); +        ATRACE_NAME(message.c_str()); +    } + +    InputMessage msg; +    msg.header.type = InputMessage::Type::TOUCH_MODE; +    msg.header.seq = seq; +    msg.body.touchMode.eventId = eventId; +    msg.body.touchMode.isInTouchMode = isInTouchMode; +    return mChannel->sendMessage(&msg); +} +  android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {      if (DEBUG_TRANSPORT_ACTIONS) {          ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__); @@ -866,6 +889,16 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum                  *outEvent = dragEvent;                  break;              } + +            case InputMessage::Type::TOUCH_MODE: { +                TouchModeEvent* touchModeEvent = factory->createTouchModeEvent(); +                if (!touchModeEvent) return NO_MEMORY; + +                initializeTouchModeEvent(touchModeEvent, &mMsg); +                *outSeq = mMsg.header.seq; +                *outEvent = touchModeEvent; +                break; +            }          }      }      return OK; @@ -1370,6 +1403,10 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage                        msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);  } +void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) { +    event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode); +} +  void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {      uint32_t pointerCount = msg->body.motion.pointerCount;      PointerCoords pointerCoords[pointerCount]; @@ -1476,6 +1513,11 @@ std::string InputConsumer::dump() const {                                                         presentTime);                      break;                  } +                case InputMessage::Type::TOUCH_MODE: { +                    out += android::base::StringPrintf("isInTouchMode=%s", +                                                       toString(msg.body.touchMode.isInTouchMode)); +                    break; +                }              }              out += "\n";          } diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 5d1f2c3bfc..8db5bf1289 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -56,6 +56,7 @@ protected:      void PublishAndConsumeFocusEvent();      void PublishAndConsumeCaptureEvent();      void PublishAndConsumeDragEvent(); +    void PublishAndConsumeTouchModeEvent();  };  TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -413,6 +414,46 @@ void InputPublisherAndConsumerTest::PublishAndConsumeDragEvent() {              << "finished signal's consume time should be greater than publish time";  } +void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() { +    status_t status; + +    constexpr uint32_t seq = 15; +    int32_t eventId = InputEvent::nextId(); +    constexpr bool touchModeEnabled = true; +    const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + +    status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled); +    ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK"; + +    uint32_t consumeSeq; +    InputEvent* event; +    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); +    ASSERT_EQ(OK, status) << "consumer consume should return OK"; + +    ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; +    ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType()) +            << "consumer should have returned a touch mode event"; + +    const TouchModeEvent& touchModeEvent = static_cast<const TouchModeEvent&>(*event); +    EXPECT_EQ(seq, consumeSeq); +    EXPECT_EQ(eventId, touchModeEvent.getId()); +    EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode()); + +    status = mConsumer->sendFinishedSignal(seq, true); +    ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + +    Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); +    ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; +    ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); +    const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); +    ASSERT_EQ(seq, finish.seq) +            << "receiveConsumerResponse should have returned the original sequence number"; +    ASSERT_TRUE(finish.handled) +            << "receiveConsumerResponse should have set handled to consumer's reply"; +    ASSERT_GE(finish.consumeTime, publishTime) +            << "finished signal's consume time should be greater than publish time"; +} +  TEST_F(InputPublisherAndConsumerTest, SendTimeline) {      const int32_t inputEventId = 20;      std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; @@ -449,6 +490,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishDragEvent_EndToEnd) {      ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());  } +TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) { +    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent()); +} +  TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {      status_t status;      const size_t pointerCount = 1; @@ -520,6 +565,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {      ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());      ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());      ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); +    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());  }  } // namespace android diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 59fed1fb41..18289a5f11 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -102,6 +102,10 @@ void TestInputMessageAlignment() {    CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);    CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);    CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8); + +  CHECK_OFFSET(InputMessage::Body::TouchMode, eventId, 0); +  CHECK_OFFSET(InputMessage::Body::TouchMode, isInTouchMode, 4); +  CHECK_OFFSET(InputMessage::Body::TouchMode, empty, 5);  }  void TestHeaderSize() { @@ -123,6 +127,7 @@ void TestBodySize() {      static_assert(sizeof(InputMessage::Body::Focus) == 8);      static_assert(sizeof(InputMessage::Body::Capture) == 8);      static_assert(sizeof(InputMessage::Body::Drag) == 16); +    static_assert(sizeof(InputMessage::Body::TouchMode) == 8);      // Timeline      static_assert(GraphicsTimeline::SIZE == 2);      static_assert(sizeof(InputMessage::Body::Timeline) == 24); diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index 2dd6c4fcaa..6a14d4f3d1 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -305,8 +305,9 @@ void Choreographer::scheduleLatestConfigRequest() {          // Fortunately, these events are small so sending packets across the          // socket should be atomic across processes.          DisplayEventReceiver::Event event; -        event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, -                                                           PhysicalDisplayId(0), systemTime()}; +        event.header = +                DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL, +                                                    PhysicalDisplayId::fromPort(0), systemTime()};          injectEvent(event);      }  } diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index 6eaa84e225..bac44c978a 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -272,10 +272,11 @@ public:      status_t attachToContext(uint32_t tex);      sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace, -                                    float* outTransformMatrix, bool* outQueueEmpty, +                                    float* outTransformMatrix, uint32_t* outTransform, +                                    bool* outQueueEmpty,                                      SurfaceTexture_createReleaseFence createFence,                                      SurfaceTexture_fenceWait fenceWait, -                                    void* fencePassThroughHandle); +                                    void* fencePassThroughHandle, ARect* currentCrop);      /**       * takeConsumerOwnership attaches a SurfaceTexture that is currently in the diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h index 85fe42f6fd..e85009c206 100644 --- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h +++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h @@ -84,10 +84,11 @@ typedef int (*ASurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle   */  AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,                                                 android_dataspace* outDataspace, -                                               float* outTransformMatrix, bool* outNewContent, +                                               float* outTransformMatrix, uint32_t* outTransform, +                                               bool* outNewContent,                                                 ASurfaceTexture_createReleaseFence createFence,                                                 ASurfaceTexture_fenceWait fenceWait, -                                               void* fencePassThroughHandle); +                                               void* fencePassThroughHandle, ARect* currentCrop);  } // namespace android diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index 62db6d069f..3535e67895 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -464,10 +464,11 @@ void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {  }  sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace, -                                                float* outTransformMatrix, bool* outQueueEmpty, +                                                float* outTransformMatrix, uint32_t* outTransform, +                                                bool* outQueueEmpty,                                                  SurfaceTexture_createReleaseFence createFence,                                                  SurfaceTexture_fenceWait fenceWait, -                                                void* fencePassThroughHandle) { +                                                void* fencePassThroughHandle, ARect* currentCrop) {      Mutex::Autolock _l(mMutex);      sp<GraphicBuffer> buffer; @@ -484,6 +485,8 @@ sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspac      buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,                                            createFence, fenceWait, fencePassThroughHandle);      memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +    *outTransform = mCurrentTransform; +    *currentCrop = mCurrentCrop;      return buffer;  } diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp index c214ab7718..cc0a12d2c6 100644 --- a/libs/nativedisplay/surfacetexture/surface_texture.cpp +++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp @@ -194,15 +194,18 @@ void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {  AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,                                                 android_dataspace* outDataspace, -                                               float* outTransformMatrix, bool* outNewContent, +                                               float* outTransformMatrix, uint32_t* outTransform, +                                               bool* outNewContent,                                                 ASurfaceTexture_createReleaseFence createFence, -                                               ASurfaceTexture_fenceWait fenceWait, void* handle) { +                                               ASurfaceTexture_fenceWait fenceWait, void* handle, +                                               ARect* currentCrop) {      sp<GraphicBuffer> buffer;      *outNewContent = false;      bool queueEmpty;      do {          buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix, -                                             &queueEmpty, createFence, fenceWait, handle); +                                             outTransform, &queueEmpty, createFence, fenceWait, +                                             handle, currentCrop);          if (!queueEmpty) {              *outNewContent = true;          } diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index d93a84cd25..d5e7cb299b 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -556,6 +556,7 @@ int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* _Nonnull buffer, uint64_t us                                     int32_t* _Nonnull outBytesPerPixel,                                     int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29); +  /**   * Get the system wide unique id for an AHardwareBuffer.   * diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 467f848237..b1e1014fe4 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -1156,10 +1156,6 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,      const mat4 projectionMatrix =              ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix; -    if (!display.clearRegion.isEmpty()) { -        glDisable(GL_BLEND); -        fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); -    }      Mesh mesh = Mesh::Builder()                          .setPrimitive(Mesh::TRIANGLE_FAN) diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index 53fa622ad8..d395d06b95 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -51,10 +51,6 @@ struct DisplaySettings {      // dataspace, in non-linear space.      mat4 colorTransform = mat4(); -    // Region that will be cleared to (0, 0, 0, 1) prior to rendering. -    // This is specified in layer-stack space. -    Region clearRegion = Region::INVALID_REGION; -      // An additional orientation flag to be applied after clipping the output.      // By way of example, this may be used for supporting fullscreen screenshot      // capture of a device in landscape while the buffer is in portrait @@ -68,8 +64,7 @@ struct DisplaySettings {  static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {      return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&              lhs.maxLuminance == rhs.maxLuminance && lhs.outputDataspace == rhs.outputDataspace && -            lhs.colorTransform == rhs.colorTransform && -            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation; +            lhs.colorTransform == rhs.colorTransform && lhs.orientation == rhs.orientation;  }  // Defining PrintTo helps with Google Tests. @@ -84,9 +79,6 @@ static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os)      PrintTo(settings.outputDataspace, os);      *os << "\n    .colorTransform = " << settings.colorTransform;      *os << "\n    .clearRegion = "; -    PrintTo(settings.clearRegion, os); -    *os << "\n    .orientation = " << settings.orientation; -    *os << "\n}";  }  } // namespace renderengine diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index ae8f2384c4..01df6a69e5 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -95,25 +95,28 @@ static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettin              .alpha = 1,      }; +    base::unique_fd drawFence;      auto layers = std::vector<const LayerSettings*>{&layer, &caster}; -    // When sourceDataspace matches dest, the general shadow fragment shader doesn't -    // have color correction added. -    // independently, when it is not srgb, the *vertex* shader has color correction added. -    // This may be a bug, but the shader still needs to be cached as it is triggered -    // during youtube pip. -    for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) { -        layer.sourceDataspace = dataspace; -        // The 2nd matrix, which has different scales for x and y, will -        // generate the slower (more general case) shadow shader -        for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) { -            layer.geometry.positionTransform = transform; -            caster.geometry.positionTransform = transform; -            for (bool translucent : {false, true}){ -                layer.shadow.casterIsTranslucent = translucent; -                renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, -                                        base::unique_fd(), nullptr); -            } -        } +    // Four combinations of settings are used (two transforms here, and drawShadowLayers is +    // called with two different destination data spaces) They're all rounded rect. +    // Three of these are cache misses that generate new shaders. +    // The first combination generates a short and simple shadow shader. +    // The second combination, flip transform, generates two shaders. The first appears to involve +    //   gaussian_fp. The second is a long and general purpose shadow shader with a device space +    //   transformation stage. +    // The third combination is a cache hit, nothing new. +    // The fourth combination, flip transform with a non-SRGB destination dataspace, is new. +    //   It is unique in that nearly everything is done in the vertex shader, and that vertex shader +    //   requires color correction. This is triggered differently from every other instance of color +    //   correction. All other instances are triggered when src and dst dataspaces differ, while +    //   this one is triggered by the destination being non-srgb. Apparently since the third +    //   combination is a cache hit, this color correction is only added when the vertex shader is +    //   doing something non-trivial. +    for (auto transform : {mat4(), kFlip}) { +        layer.geometry.positionTransform = transform; +        caster.geometry.positionTransform = transform; +        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, +                                base::unique_fd(), &drawFence);      }  } @@ -138,6 +141,7 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting                                            }},      }; +    base::unique_fd drawFence;      auto layers = std::vector<const LayerSettings*>{&layer};      for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {          layer.sourceDataspace = dataspace; @@ -151,7 +155,7 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting                  for (auto alpha : {half(.2f), half(1.0f)}) {                      layer.alpha = alpha;                      renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, -                                             base::unique_fd(), nullptr); +                                             base::unique_fd(), &drawFence);                  }              }          } @@ -174,13 +178,14 @@ static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySetting              .alpha = 0.5,      }; +    base::unique_fd drawFence;      auto layers = std::vector<const LayerSettings*>{&layer};      for (auto transform : {mat4(), kScaleAndTranslate}) {          layer.geometry.positionTransform = transform;          for (float roundedCornersRadius : {0.0f, 50.f}) {              layer.geometry.roundedCornersRadius = roundedCornersRadius;              renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, -                                     base::unique_fd(), nullptr); +                                     base::unique_fd(), &drawFence);          }      }  } @@ -199,12 +204,13 @@ static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings              .skipContentDraw = true,      }; +    base::unique_fd drawFence;      auto layers = std::vector<const LayerSettings*>{&layer};      // Different blur code is invoked for radii less and greater than 30 pixels      for (int radius : {9, 60}) {          layer.backgroundBlurRadius = radius;          renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, -                                 base::unique_fd(), nullptr); +                                 base::unique_fd(), &drawFence);      }  } @@ -240,6 +246,7 @@ static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySetti                      },      }; +    base::unique_fd drawFence;      auto layers = std::vector<const LayerSettings*>{&layer};      for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {          layer.source = pixelSource; @@ -251,7 +258,7 @@ static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySetti                  for (float alpha : {0.5f, 1.f}) {                      layer.alpha = alpha,                      renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, -                                             base::unique_fd(), nullptr); +                                             base::unique_fd(), &drawFence);                  }              }          } @@ -287,9 +294,10 @@ static void drawPIPImageLayer(SkiaRenderEngine* renderengine, const DisplaySetti      }; +    base::unique_fd drawFence;      auto layers = std::vector<const LayerSettings*>{&layer};      renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, -                             base::unique_fd(), nullptr); +                             base::unique_fd(), &drawFence);  }  static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display, @@ -316,9 +324,10 @@ static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySett      }; +    base::unique_fd drawFence;      auto layers = std::vector<const LayerSettings*>{&layer};      renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, -                            base::unique_fd(), nullptr); +                            base::unique_fd(), &drawFence);  }  // @@ -381,6 +390,7 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {                                                            ExternalTexture::Usage::WRITEABLE);          drawHolePunchLayer(renderengine, display, dstTexture);          drawSolidLayers(renderengine, display, dstTexture); +          drawShadowLayers(renderengine, display, srcTexture);          drawShadowLayers(renderengine, p3Display, srcTexture); @@ -421,6 +431,14 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {          drawPIPImageLayer(renderengine, display, dstTexture, externalTexture); +        // draw one final layer synchronously to force GL submit +        LayerSettings layer{ +                .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)}, +        }; +        auto layers = std::vector<const LayerSettings*>{&layer}; +        renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, +                             base::unique_fd(), nullptr); // null drawFence makes it synchronous +          const nsecs_t timeAfter = systemTime();          const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;          const int shadersCompiled = renderengine->reportShadersCompiled(); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index f4b07d06a5..9fbbdc34ba 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -812,28 +812,6 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,      canvas->clear(SK_ColorTRANSPARENT);      initCanvas(canvas, display); -    // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the -    // view is still on-screen. The clear region could be re-specified as a black color layer, -    // however. -    if (!display.clearRegion.isEmpty()) { -        ATRACE_NAME("ClearRegion"); -        size_t numRects = 0; -        Rect const* rects = display.clearRegion.getArray(&numRects); -        SkIRect skRects[numRects]; -        for (int i = 0; i < numRects; ++i) { -            skRects[i] = -                    SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom); -        } -        SkRegion clearRegion; -        SkPaint paint; -        sk_sp<SkShader> shader = -                SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0}, -                                 toSkColorSpace(dstDataspace)); -        paint.setShader(shader); -        clearRegion.setRects(skRects, numRects); -        canvas->drawRegion(clearRegion, paint); -    } -      for (const auto& layer : layers) {          ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str()); @@ -1149,6 +1127,73 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) {      return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);  } +/** + *  Verifies that common, simple bounds + clip combinations can be converted into + *  a single RRect draw call returning true if possible. If true the radii parameter + *  will be filled with the correct radii values that combined with bounds param will + *  produce the insected roundRect. If false, the returned state of the radii param is undefined. + */ +static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop, +                                    const SkRect& insetCrop, float cornerRadius, +                                    SkVector radii[4]) { +    const bool leftEqual = bounds.fLeft == crop.fLeft; +    const bool topEqual = bounds.fTop == crop.fTop; +    const bool rightEqual = bounds.fRight == crop.fRight; +    const bool bottomEqual = bounds.fBottom == crop.fBottom; + +    // In the event that the corners of the bounds only partially align with the crop we +    // need to ensure that the resulting shape can still be represented as a round rect. +    // In particular the round rect implementation will scale the value of all corner radii +    // if the sum of the radius along any edge is greater than the length of that edge. +    // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap +    const bool requiredWidth = bounds.width() > (cornerRadius * 2); +    const bool requiredHeight = bounds.height() > (cornerRadius * 2); +    if (!requiredWidth || !requiredHeight) { +        return false; +    } + +    // Check each cropped corner to ensure that it exactly matches the crop or its corner is +    // contained within the cropped shape and does not need rounded. +    // compute the UpperLeft corner radius +    if (leftEqual && topEqual) { +        radii[0].set(cornerRadius, cornerRadius); +    } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || +               (topEqual && bounds.fLeft >= insetCrop.fLeft)) { +        radii[0].set(0, 0); +    } else { +        return false; +    } +    // compute the UpperRight corner radius +    if (rightEqual && topEqual) { +        radii[1].set(cornerRadius, cornerRadius); +    } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || +               (topEqual && bounds.fRight <= insetCrop.fRight)) { +        radii[1].set(0, 0); +    } else { +        return false; +    } +    // compute the BottomRight corner radius +    if (rightEqual && bottomEqual) { +        radii[2].set(cornerRadius, cornerRadius); +    } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || +               (bottomEqual && bounds.fRight <= insetCrop.fRight)) { +        radii[2].set(0, 0); +    } else { +        return false; +    } +    // compute the BottomLeft corner radius +    if (leftEqual && bottomEqual) { +        radii[3].set(cornerRadius, cornerRadius); +    } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || +               (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) { +        radii[3].set(0, 0); +    } else { +        return false; +    } + +    return true; +} +  inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,                                                                          const FloatRect& cropRect,                                                                          const float cornerRadius) { @@ -1166,66 +1211,20 @@ inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const Fl          // converting them to a single RRect draw. It is possible there are other cases          // that can be converted.          if (crop.contains(bounds)) { -            bool intersectionIsRoundRect = true; -            // check each cropped corner to ensure that it exactly matches the crop or is full -            SkVector radii[4]; -              const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); - -            const bool leftEqual = bounds.fLeft == crop.fLeft; -            const bool topEqual = bounds.fTop == crop.fTop; -            const bool rightEqual = bounds.fRight == crop.fRight; -            const bool bottomEqual = bounds.fBottom == crop.fBottom; - -            // compute the UpperLeft corner radius -            if (leftEqual && topEqual) { -                radii[0].set(cornerRadius, cornerRadius); -            } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || -                       (topEqual && bounds.fLeft >= insetCrop.fLeft) || -                       insetCrop.contains(bounds.fLeft, bounds.fTop)) { -                radii[0].set(0, 0); -            } else { -                intersectionIsRoundRect = false; -            } -            // compute the UpperRight corner radius -            if (rightEqual && topEqual) { -                radii[1].set(cornerRadius, cornerRadius); -            } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || -                       (topEqual && bounds.fRight <= insetCrop.fRight) || -                       insetCrop.contains(bounds.fRight, bounds.fTop)) { -                radii[1].set(0, 0); -            } else { -                intersectionIsRoundRect = false; -            } -            // compute the BottomRight corner radius -            if (rightEqual && bottomEqual) { -                radii[2].set(cornerRadius, cornerRadius); -            } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || -                       (bottomEqual && bounds.fRight <= insetCrop.fRight) || -                       insetCrop.contains(bounds.fRight, bounds.fBottom)) { -                radii[2].set(0, 0); -            } else { -                intersectionIsRoundRect = false; -            } -            // compute the BottomLeft corner radius -            if (leftEqual && bottomEqual) { -                radii[3].set(cornerRadius, cornerRadius); -            } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || -                       (bottomEqual && bounds.fLeft >= insetCrop.fLeft) || -                       insetCrop.contains(bounds.fLeft, bounds.fBottom)) { -                radii[3].set(0, 0); -            } else { -                intersectionIsRoundRect = false; +            if (insetCrop.contains(bounds)) { +                return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required              } -            if (intersectionIsRoundRect) { +            SkVector radii[4]; +            if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {                  SkRRect intersectionBounds;                  intersectionBounds.setRectRadii(bounds, radii);                  return {intersectionBounds, clip};              }          } -        // we didn't it any of our fast paths so set the clip to the cropRect +        // we didn't hit any of our fast paths so set the clip to the cropRect          clip.setRectXY(crop, cornerRadius, cornerRadius);      } diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 33e3773d50..33053a0a61 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -524,10 +524,6 @@ public:      void fillGreenColorBufferThenClearRegion(); -    void clearLeftRegion(); - -    void clearRegion(); -      template <typename SourceVariant>      void drawShadow(const renderengine::LayerSettings& castingLayer,                      const renderengine::ShadowSettings& shadow, const ubyte4& casterColor, @@ -1195,28 +1191,6 @@ void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {      expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);  } -void RenderEngineTest::clearLeftRegion() { -    renderengine::DisplaySettings settings; -    settings.physicalDisplay = fullscreenRect(); -    // Here logical space is 4x4 -    settings.clip = Rect(4, 4); -    settings.clearRegion = Region(Rect(2, 4)); -    std::vector<const renderengine::LayerSettings*> layers; -    // fake layer, without bounds should not render anything -    renderengine::LayerSettings layer; -    layers.push_back(&layer); -    invokeDraw(settings, layers); -} - -void RenderEngineTest::clearRegion() { -    // Reuse mBuffer -    clearLeftRegion(); -    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255); -    expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, -                           DEFAULT_DISPLAY_HEIGHT), -                      0, 0, 0, 0); -} -  template <typename SourceVariant>  void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,                                    const renderengine::ShadowSettings& shadow, @@ -1647,11 +1621,6 @@ TEST_P(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) {      fillBufferWithoutPremultiplyAlpha();  } -TEST_P(RenderEngineTest, drawLayers_clearRegion) { -    initializeRenderEngine(); -    clearRegion(); -} -  TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {      initializeRenderEngine(); @@ -1900,6 +1869,40 @@ TEST_P(RenderEngineTest, testRoundedCornersParentCrop) {      expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);  } +TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) { +    initializeRenderEngine(); + +    renderengine::DisplaySettings settings; +    settings.physicalDisplay = fullscreenRect(); +    settings.clip = fullscreenRect(); +    settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + +    std::vector<const renderengine::LayerSettings*> layers; + +    renderengine::LayerSettings redLayer; +    redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; +    redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32); +    redLayer.geometry.roundedCornersRadius = 64; +    redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128); +    // Red background. +    redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f); +    redLayer.alpha = 1.0f; + +    layers.push_back(&redLayer); +    invokeDraw(settings, layers); + +    // Due to roundedCornersRadius, the top corners are untouched. +    expectBufferColor(Point(0, 0), 0, 0, 0, 0); +    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0); + +    // ensure that the entire height of the red layer was clipped by the rounded corners crop. +    expectBufferColor(Point(0, 31), 0, 0, 0, 0); +    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0); + +    // the bottom middle should be red +    expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255); +} +  TEST_P(RenderEngineTest, testClear) {      initializeRenderEngine(); diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index eed58c5715..ba5a64f317 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -137,7 +137,6 @@ cc_library_shared {          "HdrCapabilities.cpp",          "PixelFormat.cpp",          "PublicFormat.cpp", -        "Size.cpp",          "StaticDisplayInfo.cpp",      ], diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h index f196ab901a..9120972a42 100644 --- a/libs/ui/include/ui/DisplayId.h +++ b/libs/ui/include/ui/DisplayId.h @@ -38,12 +38,22 @@ struct DisplayId {      uint64_t value; +    // For deserialization. +    static constexpr std::optional<DisplayId> fromValue(uint64_t); + +    // As above, but also upcast to Id. +    template <typename Id> +    static constexpr std::optional<Id> fromValue(uint64_t value) { +        if (const auto id = Id::tryCast(DisplayId(value))) { +            return id; +        } +        return {}; +    } +  protected:      explicit constexpr DisplayId(uint64_t id) : value(id) {}  }; -static_assert(sizeof(DisplayId) == sizeof(uint64_t)); -  inline bool operator==(DisplayId lhs, DisplayId rhs) {      return lhs.value == rhs.value;  } @@ -80,11 +90,8 @@ struct PhysicalDisplayId : DisplayId {      // TODO(b/162612135) Remove default constructor      PhysicalDisplayId() = default; -    // TODO(b/162612135) Remove constructor -    explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}      constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); } -      constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }  private: @@ -96,10 +103,9 @@ private:      explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}  }; -static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t)); -  struct VirtualDisplayId : DisplayId {      using BaseId = uint32_t; +      // Flag indicating that this virtual display is backed by the GPU.      static constexpr uint64_t FLAG_GPU = 1ULL << 61; @@ -163,10 +169,23 @@ private:      explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}  }; +constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) { +    if (const auto id = fromValue<PhysicalDisplayId>(value)) { +        return id; +    } +    if (const auto id = fromValue<VirtualDisplayId>(value)) { +        return id; +    } +    return {}; +} + +static_assert(sizeof(DisplayId) == sizeof(uint64_t)); +static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));  static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t)); + +static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));  static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));  static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t)); -static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));  } // namespace android diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h index 70a0d50611..98ee35652a 100644 --- a/libs/ui/include/ui/DisplayState.h +++ b/libs/ui/include/ui/DisplayState.h @@ -16,21 +16,18 @@  #pragma once +#include <ui/LayerStack.h>  #include <ui/Rotation.h>  #include <ui/Size.h> -#include <cstdint>  #include <type_traits>  namespace android::ui { -using LayerStack = uint32_t; -constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1); -  // Transactional state of physical or virtual display. Note that libgui defines  // android::DisplayState as a superset of android::ui::DisplayState.  struct DisplayState { -    LayerStack layerStack = NO_LAYER_STACK; +    LayerStack layerStack;      Rotation orientation = ROTATION_0;      Size layerStackSpaceRect;  }; diff --git a/libs/ui/include/ui/LayerStack.h b/libs/ui/include/ui/LayerStack.h new file mode 100644 index 0000000000..d6ffeb7fad --- /dev/null +++ b/libs/ui/include/ui/LayerStack.h @@ -0,0 +1,78 @@ +/* + * Copyright 2021 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. + */ + +#pragma once + +#include <cstdint> + +#include <ftl/cast.h> +#include <ftl/string.h> +#include <log/log.h> + +namespace android::ui { + +// A LayerStack identifies a Z-ordered group of layers. A layer can only be associated to a single +// LayerStack, but a LayerStack can be associated to multiple displays, mirroring the same content. +struct LayerStack { +    uint32_t id = UINT32_MAX; + +    template <typename T> +    static constexpr LayerStack fromValue(T v) { +        if (ftl::cast_safety<uint32_t>(v) == ftl::CastSafety::kSafe) { +            return {static_cast<uint32_t>(v)}; +        } + +        ALOGW("Invalid layer stack %s", ftl::to_string(v).c_str()); +        return {}; +    } +}; + +constexpr LayerStack INVALID_LAYER_STACK; +constexpr LayerStack DEFAULT_LAYER_STACK{0u}; + +inline bool operator==(LayerStack lhs, LayerStack rhs) { +    return lhs.id == rhs.id; +} + +inline bool operator!=(LayerStack lhs, LayerStack rhs) { +    return !(lhs == rhs); +} + +inline bool operator>(LayerStack lhs, LayerStack rhs) { +    return lhs.id > rhs.id; +} + +// A LayerFilter determines if a layer is included for output to a display. +struct LayerFilter { +    LayerStack layerStack; + +    // True if the layer is only output to internal displays, i.e. excluded from screenshots, screen +    // recordings, and mirroring to virtual or external displays. Used for display cutout overlays. +    bool toInternalDisplay = false; + +    // Returns true if the input filter can be output to this filter. +    bool includes(LayerFilter other) const { +        // The layer stacks must match. +        if (other.layerStack == INVALID_LAYER_STACK || other.layerStack != layerStack) { +            return false; +        } + +        // The output must be to an internal display if the input filter has that constraint. +        return !other.toInternalDisplay || toInternalDisplay; +    } +}; + +} // namespace android::ui diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h index f1e825286e..ecc192dcae 100644 --- a/libs/ui/include/ui/Size.h +++ b/libs/ui/include/ui/Size.h @@ -23,103 +23,70 @@  #include <type_traits>  #include <utility> -namespace android { -namespace ui { +namespace android::ui { -// Forward declare a few things. -struct Size; -bool operator==(const Size& lhs, const Size& rhs); - -/** - * A simple value type representing a two-dimensional size - */ +// A simple value type representing a two-dimensional size.  struct Size { -    int32_t width; -    int32_t height; +    int32_t width = -1; +    int32_t height = -1; -    // Special values -    static const Size INVALID; -    static const Size EMPTY; +    constexpr Size() = default; -    // ------------------------------------------------------------------------ -    // Construction -    // ------------------------------------------------------------------------ - -    Size() : Size(INVALID) {}      template <typename T> -    Size(T&& w, T&& h) -          : width(Size::clamp<int32_t, T>(std::forward<T>(w))), -            height(Size::clamp<int32_t, T>(std::forward<T>(h))) {} - -    // ------------------------------------------------------------------------ -    // Accessors -    // ------------------------------------------------------------------------ +    constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {}      int32_t getWidth() const { return width; }      int32_t getHeight() const { return height; } +    // Valid means non-negative width and height +    bool isValid() const { return width >= 0 && height >= 0; } + +    // Empty means zero width and height +    bool isEmpty() const; +      template <typename T> -    void setWidth(T&& v) { -        width = Size::clamp<int32_t, T>(std::forward<T>(v)); +    void setWidth(T v) { +        width = clamp<int32_t>(v);      } +      template <typename T> -    void setHeight(T&& v) { -        height = Size::clamp<int32_t, T>(std::forward<T>(v)); +    void setHeight(T v) { +        height = clamp<int32_t>(v);      } -    // ------------------------------------------------------------------------ -    // Assignment -    // ------------------------------------------------------------------------ +    void set(Size size) { *this = size; } -    void set(const Size& size) { *this = size; }      template <typename T> -    void set(T&& w, T&& h) { -        set(Size(std::forward<T>(w), std::forward<T>(h))); +    void set(T w, T h) { +        set(Size(w, h));      } -    // Sets the value to INVALID -    void makeInvalid() { set(INVALID); } +    // Sets the value to kInvalidSize +    void makeInvalid(); -    // Sets the value to EMPTY -    void clear() { set(EMPTY); } +    // Sets the value to kEmptySize +    void clear(); -    // ------------------------------------------------------------------------ -    // Semantic checks -    // ------------------------------------------------------------------------ - -    // Valid means non-negative width and height -    bool isValid() const { return width >= 0 && height >= 0; } - -    // Empty means zero width and height -    bool isEmpty() const { return *this == EMPTY; } - -    // ------------------------------------------------------------------------ -    // Clamp Helpers -    // ------------------------------------------------------------------------ - -    // Note: We use only features available in C++11 here for compatibility with -    // external targets which include this file directly or indirectly and which -    // themselves use C++11. - -    // C++11 compatible replacement for std::remove_cv_reference_t [C++20] +    // TODO: Replace with std::remove_cvref_t in C++20.      template <typename T> -    using remove_cv_reference_t = -            typename std::remove_cv<typename std::remove_reference<T>::type>::type; +    using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;      // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,      // clamping the input value to the output range if necessary.      template <typename ToType, typename FromType> -    static Size::remove_cv_reference_t<ToType> -    clamp(typename std::enable_if< -            std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized && -                    std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized, -            FromType>::type v) { -        using BareToType = remove_cv_reference_t<ToType>; -        using BareFromType = remove_cv_reference_t<FromType>; -        static constexpr auto toHighest = std::numeric_limits<BareToType>::max(); -        static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest(); -        static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max(); -        static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest(); +    static constexpr remove_cvref_t<ToType> clamp(FromType v) { +        using BareToType = remove_cvref_t<ToType>; +        using ToLimits = std::numeric_limits<BareToType>; + +        using BareFromType = remove_cvref_t<FromType>; +        using FromLimits = std::numeric_limits<BareFromType>; + +        static_assert(ToLimits::is_specialized && FromLimits::is_specialized); + +        constexpr auto toHighest = ToLimits::max(); +        constexpr auto toLowest = ToLimits::lowest(); +        constexpr auto fromHighest = FromLimits::max(); +        constexpr auto fromLowest = FromLimits::lowest();          // Get the closest representation of [toLowest, toHighest] in type          // FromType to use to clamp the input value before conversion. @@ -127,37 +94,35 @@ struct Size {          // std::common_type<...> is used to get a value-preserving type for the          // top end of the range.          using CommonHighestType = std::common_type_t<BareToType, BareFromType>; +        using CommonLimits = std::numeric_limits<CommonHighestType>;          // std::make_signed<std::common_type<...>> is used to get a          // value-preserving type for the bottom end of the range, except this is          // a bit trickier for non-integer types like float. -        using CommonLowestType = -                std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer, -                                   std::make_signed_t<std::conditional_t< -                                           std::numeric_limits<CommonHighestType>::is_integer, -                                           CommonHighestType, int /* not used */>>, -                                   CommonHighestType>; +        using CommonLowestType = std::conditional_t< +                CommonLimits::is_integer, +                std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType, +                                                      int /* not used */>>, +                CommonHighestType>;          // We can then compute the clamp range in a way that can be later          // trivially converted to either the 'from' or 'to' types, and be -        // representabile in either. -        static constexpr auto commonClampHighest = -                std::min(static_cast<CommonHighestType>(fromHighest), -                         static_cast<CommonHighestType>(toHighest)); -        static constexpr auto commonClampLowest = -                std::max(static_cast<CommonLowestType>(fromLowest), -                         static_cast<CommonLowestType>(toLowest)); +        // representable in either. +        constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest), +                                                     static_cast<CommonHighestType>(toHighest)); +        constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest), +                                                    static_cast<CommonLowestType>(toLowest)); -        static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest); -        static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest); +        constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest); +        constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);          // A clamp is needed only if the range we are clamping to is not the          // same as the range of the input. -        static constexpr bool isClampNeeded = +        constexpr bool isClampNeeded =                  (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);          // If a clamp is not needed, the conversion is just a trivial cast. -        if (!isClampNeeded) { +        if constexpr (!isClampNeeded) {              return static_cast<BareToType>(v);          } @@ -170,34 +135,46 @@ struct Size {          // Otherwise clamping is done by using the already computed endpoints          // for each type. -        return (v <= fromClampLowest) -                ? toClampLowest -                : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v)); +        if (v <= fromClampLowest) { +            return toClampLowest; +        } + +        return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v);      }  }; -// ------------------------------------------------------------------------ -// Comparisons -// ------------------------------------------------------------------------ +constexpr Size kInvalidSize; +constexpr Size kEmptySize{0, 0}; + +inline void Size::makeInvalid() { +    set(kInvalidSize); +} -inline bool operator==(const Size& lhs, const Size& rhs) { +inline void Size::clear() { +    set(kEmptySize); +} + +inline bool operator==(Size lhs, Size rhs) {      return lhs.width == rhs.width && lhs.height == rhs.height;  } -inline bool operator!=(const Size& lhs, const Size& rhs) { -    return !operator==(lhs, rhs); +inline bool Size::isEmpty() const { +    return *this == kEmptySize; +} + +inline bool operator!=(Size lhs, Size rhs) { +    return !(lhs == rhs);  } -inline bool operator<(const Size& lhs, const Size& rhs) { +inline bool operator<(Size lhs, Size rhs) {      // Orders by increasing width, then height.      if (lhs.width != rhs.width) return lhs.width < rhs.width;      return lhs.height < rhs.height;  }  // Defining PrintTo helps with Google Tests. -static inline void PrintTo(const Size& size, ::std::ostream* os) { -    *os << "Size(" << size.width << ", " << size.height << ")"; +inline void PrintTo(Size size, std::ostream* stream) { +    *stream << "Size(" << size.width << ", " << size.height << ')';  } -} // namespace ui -} // namespace android +} // namespace android::ui diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp index 1d908b8ef1..8ddee7e740 100644 --- a/libs/ui/tests/DisplayId_test.cpp +++ b/libs/ui/tests/DisplayId_test.cpp @@ -32,6 +32,9 @@ TEST(DisplayIdTest, createPhysicalIdFromEdid) {      EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));      EXPECT_TRUE(PhysicalDisplayId::tryCast(id));      EXPECT_TRUE(HalDisplayId::tryCast(id)); + +    EXPECT_EQ(id, DisplayId::fromValue(id.value)); +    EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));  }  TEST(DisplayIdTest, createPhysicalIdFromPort) { @@ -43,6 +46,9 @@ TEST(DisplayIdTest, createPhysicalIdFromPort) {      EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));      EXPECT_TRUE(PhysicalDisplayId::tryCast(id));      EXPECT_TRUE(HalDisplayId::tryCast(id)); + +    EXPECT_EQ(id, DisplayId::fromValue(id.value)); +    EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));  }  TEST(DisplayIdTest, createGpuVirtualId) { @@ -52,6 +58,9 @@ TEST(DisplayIdTest, createGpuVirtualId) {      EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));      EXPECT_FALSE(PhysicalDisplayId::tryCast(id));      EXPECT_FALSE(HalDisplayId::tryCast(id)); + +    EXPECT_EQ(id, DisplayId::fromValue(id.value)); +    EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));  }  TEST(DisplayIdTest, createHalVirtualId) { @@ -61,6 +70,9 @@ TEST(DisplayIdTest, createHalVirtualId) {      EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));      EXPECT_FALSE(PhysicalDisplayId::tryCast(id));      EXPECT_TRUE(HalDisplayId::tryCast(id)); + +    EXPECT_EQ(id, DisplayId::fromValue(id.value)); +    EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));  }  } // namespace android::ui diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp index 5f75aeabeb..acef47fb97 100644 --- a/libs/ui/tests/Size_test.cpp +++ b/libs/ui/tests/Size_test.cpp @@ -93,9 +93,8 @@ TEST(SizeTest, ValidAndEmpty) {      }      { -        const auto& s = Size::INVALID; -        EXPECT_FALSE(s.isValid()); -        EXPECT_FALSE(s.isEmpty()); +        EXPECT_FALSE(kInvalidSize.isValid()); +        EXPECT_FALSE(kInvalidSize.isEmpty());      }      { @@ -112,9 +111,8 @@ TEST(SizeTest, ValidAndEmpty) {      }      { -        const auto& s = Size::EMPTY; -        EXPECT_TRUE(s.isValid()); -        EXPECT_TRUE(s.isEmpty()); +        EXPECT_TRUE(kEmptySize.isValid()); +        EXPECT_TRUE(kEmptySize.isEmpty());      }      {  |