diff --git a/engine/src/flutter/assets/directory_asset_bundle.cc b/engine/src/flutter/assets/directory_asset_bundle.cc index 830e96d9f5..e801e04ad7 100644 --- a/engine/src/flutter/assets/directory_asset_bundle.cc +++ b/engine/src/flutter/assets/directory_asset_bundle.cc @@ -35,10 +35,8 @@ std::unique_ptr DirectoryAssetBundle::GetAsMapping( return nullptr; } - auto mapping = std::make_unique( - fml::OpenFile(descriptor_, asset_name.c_str(), fml::OpenPermission::kRead, - false /* directory */), - false /* executable */); + auto mapping = std::make_unique(fml::OpenFile( + descriptor_, asset_name.c_str(), false, fml::FilePermission::kRead)); if (mapping->GetMapping() == nullptr) { return nullptr; diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 02466d418a..1fb9302535 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -532,11 +532,61 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: engine -ORIGIN: ../../../flutter/fml/platform/darwin/scoped_block.h + ../../../LICENSE +ORIGIN: ../../../flutter/fml/platform/android/paths_android.h + ../../../LICENSE TYPE: LicenseType.bsd -FILE: ../../../flutter/fml/platform/darwin/scoped_block.h +FILE: ../../../flutter/fml/base32.cc +FILE: ../../../flutter/fml/base32.h +FILE: ../../../flutter/fml/base32_unittest.cc +FILE: ../../../flutter/fml/file.cc +FILE: ../../../flutter/fml/file.h +FILE: ../../../flutter/fml/file_unittest.cc +FILE: ../../../flutter/fml/macros.h +FILE: ../../../flutter/fml/mapping.cc +FILE: ../../../flutter/fml/message.cc +FILE: ../../../flutter/fml/message.h +FILE: ../../../flutter/fml/message_unittests.cc +FILE: ../../../flutter/fml/native_library.h +FILE: ../../../flutter/fml/paths.cc +FILE: ../../../flutter/fml/platform/android/paths_android.h +FILE: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc +FILE: ../../../flutter/fml/platform/posix/file_posix.cc +FILE: ../../../flutter/fml/platform/posix/native_library_posix.cc +FILE: ../../../flutter/fml/platform/posix/paths_posix.cc +FILE: ../../../flutter/fml/platform/win/errors_win.cc +FILE: ../../../flutter/fml/platform/win/errors_win.h +FILE: ../../../flutter/fml/platform/win/file_win.cc +FILE: ../../../flutter/fml/platform/win/native_library_win.cc +FILE: ../../../flutter/fml/platform/win/wstring_conversion.h +FILE: ../../../flutter/fml/unique_fd.cc +FILE: ../../../flutter/fml/unique_fd.h +FILE: ../../../flutter/fml/unique_object.h +FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.cc +FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.h +FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc +FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h +FILE: ../../../flutter/lib/ui/plugins/callback_cache.cc +FILE: ../../../flutter/lib/ui/plugins/callback_cache.h +FILE: ../../../flutter/shell/common/isolate_configuration.cc +FILE: ../../../flutter/shell/common/isolate_configuration.h +FILE: ../../../flutter/shell/common/persistent_cache.cc +FILE: ../../../flutter/shell/common/persistent_cache.h +FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc +FILE: ../../../flutter/shell/platform/android/android_shell_holder.h +FILE: ../../../flutter/shell/platform/android/platform_message_response_android.cc +FILE: ../../../flutter/shell/platform/android/platform_message_response_android.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm +FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_surface.h +FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.h +FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.h +FILE: ../../../flutter/shell/version/version.cc +FILE: ../../../flutter/shell/version/version.h ---------------------------------------------------------------------------------------------------- -Copyright (c) 2013 The Chromium Authors. All rights reserved. +Copyright 2018 The Flutter Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -567,51 +617,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: engine -ORIGIN: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc + ../../../LICENSE +ORIGIN: ../../../flutter/fml/platform/darwin/scoped_block.h + ../../../LICENSE TYPE: LicenseType.bsd -FILE: ../../../flutter/fml/file.h -FILE: ../../../flutter/fml/macros.h -FILE: ../../../flutter/fml/mapping.cc -FILE: ../../../flutter/fml/message.cc -FILE: ../../../flutter/fml/message.h -FILE: ../../../flutter/fml/message_unittests.cc -FILE: ../../../flutter/fml/native_library.h -FILE: ../../../flutter/fml/paths.cc -FILE: ../../../flutter/fml/platform/fuchsia/paths_fuchsia.cc -FILE: ../../../flutter/fml/platform/posix/file_posix.cc -FILE: ../../../flutter/fml/platform/posix/native_library_posix.cc -FILE: ../../../flutter/fml/platform/posix/paths_posix.cc -FILE: ../../../flutter/fml/platform/win/file_win.cc -FILE: ../../../flutter/fml/platform/win/native_library_win.cc -FILE: ../../../flutter/fml/platform/win/wstring_conversion.h -FILE: ../../../flutter/fml/unique_fd.cc -FILE: ../../../flutter/fml/unique_fd.h -FILE: ../../../flutter/fml/unique_object.h -FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.cc -FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.h -FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc -FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h -FILE: ../../../flutter/lib/ui/plugins/callback_cache.cc -FILE: ../../../flutter/lib/ui/plugins/callback_cache.h -FILE: ../../../flutter/shell/common/isolate_configuration.cc -FILE: ../../../flutter/shell/common/isolate_configuration.h -FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc -FILE: ../../../flutter/shell/platform/android/android_shell_holder.h -FILE: ../../../flutter/shell/platform/android/platform_message_response_android.cc -FILE: ../../../flutter/shell/platform/android/platform_message_response_android.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm -FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc -FILE: ../../../flutter/shell/platform/embedder/embedder_surface.h -FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.cc -FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.h -FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.cc -FILE: ../../../flutter/shell/platform/embedder/embedder_surface_software.h -FILE: ../../../flutter/shell/version/version.cc -FILE: ../../../flutter/shell/version/version.h +FILE: ../../../flutter/fml/platform/darwin/scoped_block.h ---------------------------------------------------------------------------------------------------- -Copyright 2018 The Flutter Authors. All rights reserved. +Copyright (c) 2013 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/engine/src/flutter/fml/BUILD.gn b/engine/src/flutter/fml/BUILD.gn index 94f7dafbbd..25e065c8b1 100644 --- a/engine/src/flutter/fml/BUILD.gn +++ b/engine/src/flutter/fml/BUILD.gn @@ -5,6 +5,8 @@ source_set("fml") { sources = [ "arraysize.h", + "base32.cc", + "base32.h", "build_config.h", "closure.h", "command_line.cc", @@ -12,6 +14,7 @@ source_set("fml") { "compiler_specific.h", "eintr_wrapper.h", "export.h", + "file.cc", "file.h", "icu_util.cc", "icu_util.h", @@ -104,6 +107,7 @@ source_set("fml") { "platform/android/message_loop_android.cc", "platform/android/message_loop_android.h", "platform/android/paths_android.cc", + "platform/android/paths_android.h", "platform/android/scoped_java_ref.cc", "platform/android/scoped_java_ref.h", ] @@ -139,6 +143,8 @@ source_set("fml") { if (is_win) { sources += [ + "platform/win/errors_win.cc", + "platform/win/errors_win.h", "platform/win/file_win.cc", "platform/win/mapping_win.cc", "platform/win/message_loop_win.cc", @@ -161,7 +167,9 @@ executable("fml_unittests") { testonly = true sources = [ + "base32_unittest.cc", "command_line_unittest.cc", + "file_unittest.cc", "memory/ref_counted_unittest.cc", "memory/weak_ptr_unittest.cc", "message_loop_unittests.cc", diff --git a/engine/src/flutter/fml/base32.cc b/engine/src/flutter/fml/base32.cc new file mode 100644 index 0000000000..1836ffe084 --- /dev/null +++ b/engine/src/flutter/fml/base32.cc @@ -0,0 +1,46 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/base32.h" + +#include + +#include "flutter/fml/macros.h" + +namespace fml { + +static constexpr char kEncoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +std::pair Base32Encode(StringView input) { + if (input.empty()) { + return {true, ""}; + } + + if (input.size() > std::numeric_limits::max() / 8) { + return {false, ""}; + } + + std::string output; + const size_t encoded_length = (input.size() * 8 + 4) / 5; + output.reserve(encoded_length); + + uint16_t bit_stream = (static_cast(input[0]) << 8); + size_t next_byte_index = 1; + int free_bits = 8; + + while (free_bits < 16) { + output.push_back(kEncoding[(bit_stream & 0xf800) >> 11]); + bit_stream <<= 5; + free_bits += 5; + + if (free_bits >= 8 && next_byte_index < input.size()) { + free_bits -= 8; + bit_stream += static_cast(input[next_byte_index++]) << free_bits; + } + } + + return {true, output}; +} + +} // namespace fml diff --git a/engine/src/flutter/fml/base32.h b/engine/src/flutter/fml/base32.h new file mode 100644 index 0000000000..c5bfa8f58a --- /dev/null +++ b/engine/src/flutter/fml/base32.h @@ -0,0 +1,19 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_BASE32_H_ +#define FLUTTER_FML_BASE32_H_ + +#include +#include + +#include "flutter/fml/string_view.h" + +namespace fml { + +std::pair Base32Encode(StringView input); + +} // namespace fml + +#endif // FLUTTER_FML_BASE32_H_ diff --git a/engine/src/flutter/fml/base32_unittest.cc b/engine/src/flutter/fml/base32_unittest.cc new file mode 100644 index 0000000000..4d4ab529da --- /dev/null +++ b/engine/src/flutter/fml/base32_unittest.cc @@ -0,0 +1,38 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/base32.h" +#include "gtest/gtest.h" + +TEST(Base32Test, CanEncode) { + { + auto result = fml::Base32Encode("hello"); + ASSERT_TRUE(result.first); + ASSERT_EQ(result.second, "NBSWY3DP"); + } + + { + auto result = fml::Base32Encode("helLo"); + ASSERT_TRUE(result.first); + ASSERT_EQ(result.second, "NBSWYTDP"); + } + + { + auto result = fml::Base32Encode(""); + ASSERT_TRUE(result.first); + ASSERT_EQ(result.second, ""); + } + + { + auto result = fml::Base32Encode("1"); + ASSERT_TRUE(result.first); + ASSERT_EQ(result.second, "GE"); + } + + { + auto result = fml::Base32Encode("helLo"); + ASSERT_TRUE(result.first); + ASSERT_EQ(result.second, "NBSWYTDP"); + } +} diff --git a/engine/src/flutter/fml/file.cc b/engine/src/flutter/fml/file.cc new file mode 100644 index 0000000000..e29abd62cc --- /dev/null +++ b/engine/src/flutter/fml/file.cc @@ -0,0 +1,46 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/file.h" + +#include "flutter/fml/logging.h" + +namespace fml { + +static fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory, + const std::vector& components, + FilePermission permission, + size_t index) { + FML_DCHECK(index <= components.size()); + + const char* file_path = components[index].c_str(); + + auto directory = OpenDirectory(base_directory, file_path, true, permission); + + if (!directory.is_valid()) { + return {}; + } + + if (index == components.size() - 1) { + return directory; + } + + return CreateDirectory(directory, components, permission, index + 1); +} + +fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory, + const std::vector& components, + FilePermission permission) { + if (!IsDirectory(base_directory)) { + return {}; + } + + if (components.size() == 0) { + return {}; + } + + return CreateDirectory(base_directory, components, permission, 0); +} + +} // namespace fml diff --git a/engine/src/flutter/fml/file.h b/engine/src/flutter/fml/file.h index d915a2f3e3..699dd95409 100644 --- a/engine/src/flutter/fml/file.h +++ b/engine/src/flutter/fml/file.h @@ -5,26 +5,46 @@ #ifndef FLUTTER_FML_FILE_H_ #define FLUTTER_FML_FILE_H_ +#include +#include +#include + #include "flutter/fml/macros.h" #include "flutter/fml/unique_fd.h" +#ifdef ERROR +#undef ERROR +#endif + namespace fml { -enum class OpenPermission { - kRead = 1, - kWrite = 1 << 1, - kReadWrite = kRead | kWrite, - kExecute, +class Mapping; + +enum class FilePermission { + kRead, + kWrite, + kReadWrite, }; +std::string CreateTemporaryDirectory(); + fml::UniqueFD OpenFile(const char* path, - OpenPermission permission, - bool is_directory = false); + bool create_if_necessary, + FilePermission permission); fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory, const char* path, - OpenPermission permission, - bool is_directory = false); + bool create_if_necessary, + FilePermission permission); + +fml::UniqueFD OpenDirectory(const char* path, + bool create_if_necessary, + FilePermission permission); + +fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory, + const char* path, + bool create_if_necessary, + FilePermission permission); fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor); @@ -33,6 +53,50 @@ bool IsDirectory(const fml::UniqueFD& directory); // Returns whether the given path is a file. bool IsFile(const std::string& path); +bool TruncateFile(const fml::UniqueFD& file, size_t size); + +bool FileExists(const fml::UniqueFD& base_directory, const char* path); + +bool UnlinkDirectory(const char* path); + +bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path); + +bool UnlinkFile(const char* path); + +bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path); + +fml::UniqueFD CreateDirectory(const fml::UniqueFD& base_directory, + const std::vector& components, + FilePermission permission); + +bool WriteAtomically(const fml::UniqueFD& base_directory, + const char* file_name, + const Mapping& mapping); + +class ScopedTemporaryDirectory { + public: + ScopedTemporaryDirectory() { + path_ = CreateTemporaryDirectory(); + if (path_ != "") { + dir_fd_ = OpenDirectory(path_.c_str(), false, FilePermission::kRead); + } + } + + ~ScopedTemporaryDirectory() { + if (path_ != "") { + if (!UnlinkDirectory(path_.c_str())) { + FML_LOG(ERROR) << "Could not remove directory: " << path_; + } + } + } + + const UniqueFD& fd() { return dir_fd_; } + + private: + std::string path_; + UniqueFD dir_fd_; +}; + } // namespace fml #endif // FLUTTER_FML_FILE_H_ diff --git a/engine/src/flutter/fml/file_unittest.cc b/engine/src/flutter/fml/file_unittest.cc new file mode 100644 index 0000000000..b44dca09f2 --- /dev/null +++ b/engine/src/flutter/fml/file_unittest.cc @@ -0,0 +1,157 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "gtest/gtest.h" + +#include "flutter/fml/build_config.h" +#include "flutter/fml/file.h" +#include "flutter/fml/mapping.h" + +static bool WriteStringToFile(const fml::UniqueFD& fd, + const std::string& contents) { + if (!fml::TruncateFile(fd, contents.size())) { + return false; + } + + fml::FileMapping mapping(fd, {fml::FileMapping::Protection::kWrite}); + if (mapping.GetSize() != contents.size()) { + return false; + } + + if (mapping.GetMutableMapping() == nullptr) { + return false; + } + + ::memmove(mapping.GetMutableMapping(), contents.data(), contents.size()); + return true; +} + +static std::string ReadStringFromFile(const fml::UniqueFD& fd) { + fml::FileMapping mapping(fd); + + if (mapping.GetMapping() == nullptr) { + return nullptr; + } + + return {reinterpret_cast(mapping.GetMapping()), + mapping.GetSize()}; +} + +TEST(FileTest, CreateTemporaryAndUnlink) { + auto dir_name = fml::CreateTemporaryDirectory(); + ASSERT_NE(dir_name, ""); + auto dir = + fml::OpenDirectory(dir_name.c_str(), false, fml::FilePermission::kRead); + ASSERT_TRUE(dir.is_valid()); + dir.reset(); + ASSERT_TRUE(fml::UnlinkDirectory(dir_name.c_str())); +} + +TEST(FileTest, ScopedTempDirIsValid) { + fml::ScopedTemporaryDirectory dir; + ASSERT_TRUE(dir.fd().is_valid()); +} + +TEST(FileTest, CanOpenFileForWriting) { + fml::ScopedTemporaryDirectory dir; + ASSERT_TRUE(dir.fd().is_valid()); + + auto fd = + fml::OpenFile(dir.fd(), "some.txt", true, fml::FilePermission::kWrite); + ASSERT_TRUE(fd.is_valid()); + fd.reset(); + ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "some.txt")); +} + +TEST(FileTest, CanTruncateAndWrite) { + fml::ScopedTemporaryDirectory dir; + ASSERT_TRUE(dir.fd().is_valid()); + + std::string contents = "some contents here"; + + { + auto fd = fml::OpenFile(dir.fd(), "some.txt", true, + fml::FilePermission::kReadWrite); + ASSERT_TRUE(fd.is_valid()); + + ASSERT_TRUE(fml::TruncateFile(fd, contents.size())); + + fml::FileMapping mapping(fd, {fml::FileMapping::Protection::kWrite}); + ASSERT_EQ(mapping.GetSize(), contents.size()); + ASSERT_NE(mapping.GetMutableMapping(), nullptr); + + ::memcpy(mapping.GetMutableMapping(), contents.data(), contents.size()); + } + + { + auto fd = + fml::OpenFile(dir.fd(), "some.txt", false, fml::FilePermission::kRead); + ASSERT_TRUE(fd.is_valid()); + + fml::FileMapping mapping(fd); + ASSERT_EQ(mapping.GetSize(), contents.size()); + + ASSERT_EQ(0, + ::memcmp(mapping.GetMapping(), contents.data(), contents.size())); + } + + fml::UnlinkFile(dir.fd(), "some.txt"); +} + +TEST(FileTest, CreateDirectoryStructure) { + fml::ScopedTemporaryDirectory dir; + + std::string contents = "These are my contents"; + { + auto sub = fml::CreateDirectory(dir.fd(), {"a", "b", "c"}, + fml::FilePermission::kReadWrite); + ASSERT_TRUE(sub.is_valid()); + auto file = fml::OpenFile(sub, "my_contents", true, + fml::FilePermission::kReadWrite); + ASSERT_TRUE(file.is_valid()); + ASSERT_TRUE(WriteStringToFile(file, contents)); + } + + const char* file_path = "a/b/c/my_contents"; + + { + auto contents_file = + fml::OpenFile(dir.fd(), file_path, false, fml::FilePermission::kRead); + ASSERT_EQ(ReadStringFromFile(contents_file), contents); + } + + // Cleanup. + ASSERT_TRUE(fml::UnlinkFile(dir.fd(), file_path)); + ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b/c")); + ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a/b")); + ASSERT_TRUE(fml::UnlinkDirectory(dir.fd(), "a")); +} + +#if OS_WIN +#define AtomicWriteTest DISABLED_AtomicWriteTest +#else +#define AtomicWriteTest AtomicWriteTest +#endif +TEST(FileTest, AtomicWriteTest) { + fml::ScopedTemporaryDirectory dir; + + const std::string contents = "These are my contents."; + + auto data = std::make_unique( + std::vector{contents.begin(), contents.end()}); + + // Write. + ASSERT_TRUE(fml::WriteAtomically(dir.fd(), "precious_data", *data)); + + // Read and verify. + ASSERT_EQ(contents, + ReadStringFromFile(fml::OpenFile(dir.fd(), "precious_data", false, + fml::FilePermission::kRead))); + + // Cleanup. + ASSERT_TRUE(fml::UnlinkFile(dir.fd(), "precious_data")); +} diff --git a/engine/src/flutter/fml/icu_util.cc b/engine/src/flutter/fml/icu_util.cc index 03082886e1..28402e84e3 100644 --- a/engine/src/flutter/fml/icu_util.cc +++ b/engine/src/flutter/fml/icu_util.cc @@ -16,12 +16,6 @@ namespace fml { namespace icu { -#if OS_WIN -static constexpr char kPathSeparator = '\\'; -#else -static constexpr char kPathSeparator = '/'; -#endif - class ICUContext { public: ICUContext(const std::string& icu_data_path) : valid_(false) { @@ -31,25 +25,37 @@ class ICUContext { ~ICUContext() = default; bool SetupMapping(const std::string& icu_data_path) { - // Check if the explicit path specified exists. - auto path_mapping = std::make_unique(icu_data_path, false); - if (path_mapping->GetSize() != 0) { - mapping_ = std::move(path_mapping); - return true; + // Check if the path exists and it readable directly. + auto fd = + fml::OpenFile(icu_data_path.c_str(), false, fml::FilePermission::kRead); + + // Check the path relative to the current executable. + if (!fd.is_valid()) { + auto directory = fml::paths::GetExecutableDirectoryPath(); + + if (!directory.first) { + return false; + } + + std::string path_relative_to_executable = + paths::JoinPaths({directory.second, icu_data_path}); + + fd = fml::OpenFile(path_relative_to_executable.c_str(), false, + fml::FilePermission::kRead); } - // Check if the mapping can by directly accessed via a file path. In this - // case, the data file needs to be next to the executable. - auto directory = fml::paths::GetExecutableDirectoryPath(); - - if (!directory.first) { + if (!fd.is_valid()) { return false; } - auto file = std::make_unique( - directory.second + kPathSeparator + icu_data_path, false); - if (file->GetSize() != 0) { - mapping_ = std::move(file); + std::initializer_list protection = { + fml::FileMapping::Protection::kRead}; + + auto file_mapping = + std::make_unique(fd, std::move(protection)); + + if (file_mapping->GetSize() != 0) { + mapping_ = std::move(file_mapping); return true; } diff --git a/engine/src/flutter/fml/mapping.cc b/engine/src/flutter/fml/mapping.cc index 97d7905015..332341f2fb 100644 --- a/engine/src/flutter/fml/mapping.cc +++ b/engine/src/flutter/fml/mapping.cc @@ -6,6 +6,10 @@ namespace fml { +uint8_t* FileMapping::GetMutableMapping() { + return mutable_mapping_; +} + DataMapping::DataMapping(std::vector data) : data_(std::move(data)) {} DataMapping::~DataMapping() = default; @@ -17,4 +21,5 @@ size_t DataMapping::GetSize() const { const uint8_t* DataMapping::GetMapping() const { return data_.data(); } + } // namespace fml diff --git a/engine/src/flutter/fml/mapping.h b/engine/src/flutter/fml/mapping.h index e3d840dc8c..a2a58851e5 100644 --- a/engine/src/flutter/fml/mapping.h +++ b/engine/src/flutter/fml/mapping.h @@ -5,11 +5,13 @@ #ifndef FLUTTER_FML_MAPPING_H_ #define FLUTTER_FML_MAPPING_H_ +#include #include #include #include #include "flutter/fml/build_config.h" +#include "flutter/fml/file.h" #include "flutter/fml/macros.h" #include "flutter/fml/unique_fd.h" @@ -31,9 +33,15 @@ class Mapping { class FileMapping : public Mapping { public: - FileMapping(const std::string& path, bool executable = false); + enum class Protection { + kRead, + kWrite, + kExecute, + }; - FileMapping(const fml::UniqueFD& fd, bool executable = false); + FileMapping(const fml::UniqueFD& fd, + std::initializer_list protection = { + Protection::kRead}); ~FileMapping() override; @@ -41,9 +49,12 @@ class FileMapping : public Mapping { const uint8_t* GetMapping() const override; + uint8_t* GetMutableMapping(); + private: size_t size_ = 0; uint8_t* mapping_ = nullptr; + uint8_t* mutable_mapping_ = nullptr; #if OS_WIN fml::UniqueFD mapping_handle_; @@ -68,6 +79,22 @@ class DataMapping : public Mapping { FML_DISALLOW_COPY_AND_ASSIGN(DataMapping); }; +class NonOwnedMapping : public Mapping { + public: + NonOwnedMapping(const uint8_t* data, size_t size) + : data_(data), size_(size) {} + + size_t GetSize() const override { return size_; } + + const uint8_t* GetMapping() const override { return data_; } + + private: + const uint8_t* const data_; + const size_t size_; + + FML_DISALLOW_COPY_AND_ASSIGN(NonOwnedMapping); +}; + } // namespace fml #endif // FLUTTER_FML_MAPPING_H_ diff --git a/engine/src/flutter/fml/paths.h b/engine/src/flutter/fml/paths.h index cccf635786..5bb6f8affe 100644 --- a/engine/src/flutter/fml/paths.h +++ b/engine/src/flutter/fml/paths.h @@ -8,11 +8,16 @@ #include #include +#include "flutter/fml/unique_fd.h" + namespace fml { namespace paths { std::pair GetExecutableDirectoryPath(); +// Get the directory to the application's caches directory. +fml::UniqueFD GetCachesDirectory(); + std::string JoinPaths(std::initializer_list components); // Returns the absolute path of a possibly relative path. diff --git a/engine/src/flutter/fml/platform/android/paths_android.cc b/engine/src/flutter/fml/platform/android/paths_android.cc index 535bab4c00..a6104601cd 100644 --- a/engine/src/flutter/fml/platform/android/paths_android.cc +++ b/engine/src/flutter/fml/platform/android/paths_android.cc @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "flutter/fml/paths.h" +#include "flutter/fml/platform/android/paths_android.h" + +#include "flutter/fml/file.h" namespace fml { namespace paths { @@ -11,5 +13,17 @@ std::pair GetExecutableDirectoryPath() { return {false, ""}; } +static std::string gCachesPath; + +void InitializeAndroidCachesPath(std::string caches_path) { + gCachesPath = std::move(caches_path); +} + +fml::UniqueFD GetCachesDirectory() { + // If the caches path is not initialized, the FD will be invalid and caching + // will be disabled throughout the system. + return OpenDirectory(gCachesPath.c_str(), false, fml::FilePermission::kRead); +} + } // namespace paths } // namespace fml diff --git a/engine/src/flutter/fml/platform/android/paths_android.h b/engine/src/flutter/fml/platform/android/paths_android.h new file mode 100644 index 0000000000..b9ebe2ebf7 --- /dev/null +++ b/engine/src/flutter/fml/platform/android/paths_android.h @@ -0,0 +1,19 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_PLATFORM_ANDROID_PATHS_ANDROID_H_ +#define FLUTTER_FML_PLATFORM_ANDROID_PATHS_ANDROID_H_ + +#include "flutter/fml/macros.h" +#include "flutter/fml/paths.h" + +namespace fml { +namespace paths { + +void InitializeAndroidCachesPath(std::string caches_path); + +} // namespace paths +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_ANDROID_PATHS_ANDROID_H_ diff --git a/engine/src/flutter/fml/platform/darwin/paths_darwin.mm b/engine/src/flutter/fml/platform/darwin/paths_darwin.mm index 12e3eea7f7..025eec73a1 100644 --- a/engine/src/flutter/fml/platform/darwin/paths_darwin.mm +++ b/engine/src/flutter/fml/platform/darwin/paths_darwin.mm @@ -6,11 +6,27 @@ #include +#include "flutter/fml/file.h" + namespace fml { namespace paths { std::pair GetExecutableDirectoryPath() { - return {true, GetDirectoryName([NSBundle mainBundle].executablePath.UTF8String)}; + @autoreleasepool { + return {true, GetDirectoryName([NSBundle mainBundle].executablePath.UTF8String)}; + } +} + +fml::UniqueFD GetCachesDirectory() { + @autoreleasepool { + auto items = [[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory + inDomains:NSUserDomainMask]; + if (items.count == 0) { + return {}; + } + + return OpenDirectory(items[0].fileSystemRepresentation, false, FilePermission::kRead); + } } } // namespace paths diff --git a/engine/src/flutter/fml/platform/fuchsia/paths_fuchsia.cc b/engine/src/flutter/fml/platform/fuchsia/paths_fuchsia.cc index 20def9f34f..62a2fb0854 100644 --- a/engine/src/flutter/fml/platform/fuchsia/paths_fuchsia.cc +++ b/engine/src/flutter/fml/platform/fuchsia/paths_fuchsia.cc @@ -11,5 +11,10 @@ std::pair GetExecutableDirectoryPath() { return {false, ""}; } +fml::UniqueFD GetCachesDirectory() { + // Unsupported on this platform. + return {}; +} + } // namespace paths } // namespace fml diff --git a/engine/src/flutter/fml/platform/linux/paths_linux.cc b/engine/src/flutter/fml/platform/linux/paths_linux.cc index 9871e50744..a6b1789707 100644 --- a/engine/src/flutter/fml/platform/linux/paths_linux.cc +++ b/engine/src/flutter/fml/platform/linux/paths_linux.cc @@ -22,5 +22,10 @@ std::pair GetExecutableDirectoryPath() { std::string{path, static_cast(read_size)})}; } +fml::UniqueFD GetCachesDirectory() { + // Unsupported on this platform. + return {}; +} + } // namespace paths } // namespace fml diff --git a/engine/src/flutter/fml/platform/posix/file_posix.cc b/engine/src/flutter/fml/platform/posix/file_posix.cc index e043a0f340..99a202586d 100644 --- a/engine/src/flutter/fml/platform/posix/file_posix.cc +++ b/engine/src/flutter/fml/platform/posix/file_posix.cc @@ -5,49 +5,113 @@ #include "flutter/fml/file.h" #include +#include #include #include +#include +#include + #include "flutter/fml/eintr_wrapper.h" +#include "flutter/fml/mapping.h" namespace fml { +std::string CreateTemporaryDirectory() { + char directory_name[] = "/tmp/flutter_XXXXXXXX"; + auto result = ::mkdtemp(directory_name); + if (result == nullptr) { + return ""; + } + return {result}; +} + +static int ToPosixAccessFlags(FilePermission permission) { + int flags = 0; + switch (permission) { + case FilePermission::kRead: + flags |= O_RDONLY; // read only + break; + case FilePermission::kWrite: + flags |= O_WRONLY; // write only + break; + case FilePermission::kReadWrite: + flags |= O_RDWR; // read-write + break; + } + return flags; +} + +static int ToPosixCreateModeFlags(FilePermission permission) { + int mode = 0; + switch (permission) { + case FilePermission::kRead: + mode |= S_IRUSR; + break; + case FilePermission::kWrite: + mode |= S_IWUSR; + break; + case FilePermission::kReadWrite: + mode |= S_IRUSR | S_IWUSR; + break; + } + return mode; +} + fml::UniqueFD OpenFile(const char* path, - OpenPermission permission, - bool is_directory) { - return OpenFile(fml::UniqueFD{AT_FDCWD}, path, permission, is_directory); + bool create_if_necessary, + FilePermission permission) { + return OpenFile(fml::UniqueFD{AT_FDCWD}, path, create_if_necessary, + permission); } fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory, const char* path, - OpenPermission permission, - bool is_directory) { + bool create_if_necessary, + FilePermission permission) { if (path == nullptr) { - return fml::UniqueFD{}; + return {}; } int flags = 0; - switch (permission) { - case OpenPermission::kRead: - flags = O_RDONLY; - break; - case OpenPermission::kWrite: - flags = O_WRONLY; - break; - case OpenPermission::kReadWrite: - flags = O_RDWR; - break; - case OpenPermission::kExecute: - flags = O_RDONLY; - break; - } + int mode = 0; - if (is_directory) { - flags |= O_DIRECTORY; + if (create_if_necessary && !FileExists(base_directory, path)) { + flags = ToPosixAccessFlags(permission) | O_CREAT | O_TRUNC; + mode = ToPosixCreateModeFlags(permission); + } else { + flags = ToPosixAccessFlags(permission); + mode = 0; // Not creating since it already exists. } return fml::UniqueFD{ - FML_HANDLE_EINTR(::openat(base_directory.get(), path, flags))}; + FML_HANDLE_EINTR(::openat(base_directory.get(), path, flags, mode))}; +} + +fml::UniqueFD OpenDirectory(const char* path, + bool create_if_necessary, + FilePermission permission) { + return OpenDirectory(fml::UniqueFD{AT_FDCWD}, path, create_if_necessary, + permission); +} + +fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory, + const char* path, + bool create_if_necessary, + FilePermission permission) { + if (path == nullptr) { + return {}; + } + + if (create_if_necessary && !FileExists(base_directory, path)) { + if (::mkdirat(base_directory.get(), path, + ToPosixCreateModeFlags(permission) | S_IXUSR) != 0) { + return {}; + } + } + + return fml::UniqueFD{FML_HANDLE_EINTR( + ::openat(base_directory.get(), path, O_RDONLY | O_DIRECTORY))}; } fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor) { @@ -70,9 +134,80 @@ bool IsDirectory(const fml::UniqueFD& directory) { bool IsFile(const std::string& path) { struct stat buf; - if (stat(path.c_str(), &buf) != 0) + if (stat(path.c_str(), &buf) != 0) { return false; + } + return S_ISREG(buf.st_mode); } +bool TruncateFile(const fml::UniqueFD& file, size_t size) { + if (!file.is_valid()) { + return false; + } + + return ::ftruncate(file.get(), size) == 0; +} + +bool UnlinkDirectory(const char* path) { + return UnlinkDirectory(fml::UniqueFD{AT_FDCWD}, path); +} + +bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) { + return ::unlinkat(base_directory.get(), path, AT_REMOVEDIR) == 0; +} + +bool UnlinkFile(const char* path) { + return UnlinkFile(fml::UniqueFD{AT_FDCWD}, path); +} + +bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) { + return ::unlinkat(base_directory.get(), path, 0) == 0; +} + +bool FileExists(const fml::UniqueFD& base_directory, const char* path) { + if (!base_directory.is_valid()) { + return false; + } + + return ::faccessat(base_directory.get(), path, F_OK, 0) == 0; +} + +bool WriteAtomically(const fml::UniqueFD& base_directory, + const char* file_name, + const Mapping& data) { + if (file_name == nullptr || data.GetMapping() == nullptr) { + return false; + } + + std::stringstream stream; + stream << file_name << ".temp"; + const auto temp_file_name = stream.str(); + + auto temp_file = OpenFile(base_directory, temp_file_name.c_str(), true, + FilePermission::kReadWrite); + if (!temp_file.is_valid()) { + return false; + } + + if (!TruncateFile(temp_file, data.GetSize())) { + return false; + } + + FileMapping mapping(temp_file, {FileMapping::Protection::kWrite}); + if (mapping.GetMutableMapping() == nullptr || + data.GetSize() != mapping.GetSize()) { + return false; + } + + ::memcpy(mapping.GetMutableMapping(), data.GetMapping(), data.GetSize()); + + if (::msync(mapping.GetMutableMapping(), data.GetSize(), MS_SYNC) != 0) { + return false; + } + + return ::renameat(base_directory.get(), temp_file_name.c_str(), + base_directory.get(), file_name) == 0; +} + } // namespace fml diff --git a/engine/src/flutter/fml/platform/posix/mapping_posix.cc b/engine/src/flutter/fml/platform/posix/mapping_posix.cc index e1dbdedbb5..273eb0e668 100644 --- a/engine/src/flutter/fml/platform/posix/mapping_posix.cc +++ b/engine/src/flutter/fml/platform/posix/mapping_posix.cc @@ -17,16 +17,41 @@ namespace fml { +static int ToPosixProtectionFlags( + std::initializer_list protection_flags) { + int flags = 0; + for (auto protection : protection_flags) { + switch (protection) { + case FileMapping::Protection::kRead: + flags |= PROT_READ; + break; + case FileMapping::Protection::kWrite: + flags |= PROT_WRITE; + break; + case FileMapping::Protection::kExecute: + flags |= PROT_READ | PROT_EXEC; + break; + } + } + return flags; +} + +static bool IsWritable( + std::initializer_list protection_flags) { + for (auto protection : protection_flags) { + if (protection == FileMapping::Protection::kWrite) { + return true; + } + } + return false; +} + Mapping::Mapping() = default; Mapping::~Mapping() = default; -FileMapping::FileMapping(const std::string& path, bool executable) - : FileMapping( - fml::UniqueFD{FML_HANDLE_EINTR(::open(path.c_str(), O_RDONLY))}, - executable) {} - -FileMapping::FileMapping(const fml::UniqueFD& handle, bool executable) +FileMapping::FileMapping(const fml::UniqueFD& handle, + std::initializer_list protection) : size_(0), mapping_(nullptr) { if (!handle.is_valid()) { return; @@ -42,13 +67,11 @@ FileMapping::FileMapping(const fml::UniqueFD& handle, bool executable) return; } - int flags = PROT_READ; - if (executable) { - flags |= PROT_EXEC; - } + const auto is_writable = IsWritable(protection); auto mapping = - ::mmap(nullptr, stat_buffer.st_size, flags, MAP_PRIVATE, handle.get(), 0); + ::mmap(nullptr, stat_buffer.st_size, ToPosixProtectionFlags(protection), + is_writable ? MAP_SHARED : MAP_PRIVATE, handle.get(), 0); if (mapping == MAP_FAILED) { return; @@ -56,6 +79,9 @@ FileMapping::FileMapping(const fml::UniqueFD& handle, bool executable) mapping_ = static_cast(mapping); size_ = stat_buffer.st_size; + if (is_writable) { + mutable_mapping_ = mapping_; + } } FileMapping::~FileMapping() { diff --git a/engine/src/flutter/fml/platform/win/errors_win.cc b/engine/src/flutter/fml/platform/win/errors_win.cc new file mode 100644 index 0000000000..b19b2b5e52 --- /dev/null +++ b/engine/src/flutter/fml/platform/win/errors_win.cc @@ -0,0 +1,46 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/platform/win/errors_win.h" + +#include + +#include + +#include "flutter/fml/platform/win/wstring_conversion.h" + +namespace fml { + +std::string GetLastErrorMessage() { + DWORD last_error = ::GetLastError(); + if (last_error == 0) { + return {}; + } + + const DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + + wchar_t* buffer = nullptr; + size_t size = ::FormatMessage( + flags, // dwFlags + NULL, // lpSource + last_error, // dwMessageId + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // dwLanguageId + (LPWSTR)&buffer, // lpBuffer + 0, // nSize + NULL // Arguments + ); + + std::wstring message(buffer, size); + + ::LocalFree(buffer); + + std::wstringstream stream; + stream << message << " (" << last_error << ")."; + + return WideStringToString(stream.str()); +} + +} // namespace fml diff --git a/engine/src/flutter/fml/platform/win/errors_win.h b/engine/src/flutter/fml/platform/win/errors_win.h new file mode 100644 index 0000000000..2ddf32936f --- /dev/null +++ b/engine/src/flutter/fml/platform/win/errors_win.h @@ -0,0 +1,16 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_PLATFORM_WIN_ERRORS_WIN_H_ +#define FLUTTER_FML_PLATFORM_WIN_ERRORS_WIN_H_ + +#include + +namespace fml { + +std::string GetLastErrorMessage(); + +} // namespace fml + +#endif // FLUTTER_FML_PLATFORM_WIN_ERRORS_WIN_H_ diff --git a/engine/src/flutter/fml/platform/win/file_win.cc b/engine/src/flutter/fml/platform/win/file_win.cc index 6f182ef049..cef530e804 100644 --- a/engine/src/flutter/fml/platform/win/file_win.cc +++ b/engine/src/flutter/fml/platform/win/file_win.cc @@ -4,6 +4,7 @@ #include "flutter/fml/file.h" +#include #include #include #include @@ -13,6 +14,8 @@ #include #include "flutter/fml/build_config.h" +#include "flutter/fml/mapping.h" +#include "flutter/fml/platform/win/errors_win.h" #include "flutter/fml/platform/win/wstring_conversion.h" #if defined(OS_WIN) @@ -21,85 +24,200 @@ namespace fml { -static fml::UniqueFD OpenFile(std::wstring path, - OpenPermission permission, - bool is_directory) { - if (path.size() == 0) { - return fml::UniqueFD{}; - } - - DWORD desired_access = 0; - - switch (permission) { - case OpenPermission::kRead: - desired_access = GENERIC_READ; - break; - case OpenPermission::kWrite: - desired_access = GENERIC_WRITE; - break; - case OpenPermission::kReadWrite: - desired_access = GENERIC_WRITE | GENERIC_READ; - break; - case OpenPermission::kExecute: - desired_access = GENERIC_READ | GENERIC_EXECUTE; - break; - } - - DWORD flags = FILE_ATTRIBUTE_NORMAL; - - if (is_directory) { - flags |= FILE_FLAG_BACKUP_SEMANTICS; +static std::string GetFullHandlePath(const fml::UniqueFD& handle) { + wchar_t buffer[MAX_PATH] = {0}; + const DWORD buffer_size = ::GetFinalPathNameByHandle( + handle.get(), buffer, MAX_PATH, FILE_NAME_NORMALIZED); + if (buffer_size == 0) { + FML_DLOG(ERROR) << "Could not get file handle path. " + << GetLastErrorMessage(); + return {}; } + return WideStringToString({buffer, buffer_size}); +} +static std::string GetAbsolutePath(const fml::UniqueFD& base_directory, + const char* subpath) { + std::stringstream stream; + stream << GetFullHandlePath(base_directory) << "\\" << subpath; + auto path = stream.str(); std::replace(path.begin(), path.end(), '/', '\\'); - - return fml::UniqueFD{::CreateFile(path.c_str(), // lpFileName - desired_access, // dwDesiredAccess - FILE_SHARE_READ, // dwShareMode - 0, // lpSecurityAttributes - OPEN_EXISTING, // dwCreationDisposition - flags, // dwFlagsAndAttributes - 0 // hTemplateFile - )}; + return path; } -fml::UniqueFD OpenFile(const char* path, - OpenPermission permission, - bool is_directory) { - return OpenFile(ConvertToWString(path), permission, is_directory); +static std::wstring GetTemporaryDirectoryPath() { + wchar_t wchar_path[MAX_PATH]; + auto result_size = ::GetTempPath(MAX_PATH, wchar_path); + if (result_size > 0) { + return {wchar_path, result_size}; + } + FML_DLOG(ERROR) << "Could not get temporary directory path. " + << GetLastErrorMessage(); + return {}; } -static std::wstring GetFullHandlePath(const fml::UniqueFD& handle) { - wchar_t buffer[MAX_PATH]; +static DWORD GetDesiredAccessFlags(FilePermission permission) { + switch (permission) { + case FilePermission::kRead: + return GENERIC_READ; + case FilePermission::kWrite: + return GENERIC_WRITE; + case FilePermission::kReadWrite: + return GENERIC_READ | GENERIC_WRITE; + } + return GENERIC_READ; +} - DWORD returned = ::GetFinalPathNameByHandle(handle.get(), buffer, MAX_PATH, - FILE_NAME_NORMALIZED); - if (returned == 0 || returned > MAX_PATH) { +static DWORD GetShareFlags(FilePermission permission) { + switch (permission) { + case FilePermission::kRead: + return FILE_SHARE_READ; + case FilePermission::kWrite: + return FILE_SHARE_WRITE; + case FilePermission::kReadWrite: + return FILE_SHARE_READ | FILE_SHARE_WRITE; + } + return FILE_SHARE_READ; +} + +std::string CreateTemporaryDirectory() { + // Get the system temporary directory. + auto temp_dir_container = GetTemporaryDirectoryPath(); + if (temp_dir_container.size() == 0) { + FML_DLOG(ERROR) << "Could not get system temporary directory."; return {}; } - return {buffer}; + // Create a UUID. + UUID uuid; + RPC_STATUS status = UuidCreateSequential(&uuid); + if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) { + FML_DLOG(ERROR) << "Could not create UUID"; + return {}; + } + + RPC_WSTR uuid_string; + status = UuidToString(&uuid, &uuid_string); + if (status != RPC_S_OK) { + FML_DLOG(ERROR) << "Could not create UUID to string."; + return {}; + } + + std::wstring uuid_str(reinterpret_cast(uuid_string)); + RpcStringFree(&uuid_string); + + // Join the two and create a path to the new temporary directory. + + std::wstringstream stream; + stream << temp_dir_container << "\\" << uuid_str; + auto temp_dir = stream.str(); + + auto dir_fd = OpenDirectory(WideStringToString(temp_dir).c_str(), true, + FilePermission::kReadWrite); + if (!dir_fd.is_valid()) { + FML_DLOG(ERROR) << "Could not get temporary directory FD. " + << GetLastErrorMessage(); + return {}; + } + + return WideStringToString(std::move(temp_dir)); } fml::UniqueFD OpenFile(const fml::UniqueFD& base_directory, const char* path, - OpenPermission permission, - bool is_directory) { - // If the base directory is invalid or the path is absolute, use the generic - // open file variant. - if (!base_directory.is_valid()) { - return OpenFile(path, permission, is_directory); + bool create_if_necessary, + FilePermission permission) { + return OpenFile(GetAbsolutePath(base_directory, path).c_str(), + create_if_necessary, permission); +} + +fml::UniqueFD OpenFile(const char* path, + bool create_if_necessary, + FilePermission permission) { + if (path == nullptr || strlen(path) == 0) { + return {}; } - const auto wpath = ConvertToWString(path); + auto file_name = StringToWideString({path}); - if (!::PathIsRelative(wpath.c_str())) { - return OpenFile(path, permission, is_directory); + if (file_name.size() == 0) { + return {}; } - std::wstringstream stream; - stream << GetFullHandlePath(base_directory) << "\\" << path; - return OpenFile(stream.str(), permission, is_directory); + const DWORD creation_disposition = + create_if_necessary ? CREATE_NEW : OPEN_EXISTING; + + const DWORD flags = FILE_ATTRIBUTE_NORMAL; + + auto handle = + CreateFile(file_name.c_str(), // lpFileName + GetDesiredAccessFlags(permission), // dwDesiredAccess + GetShareFlags(permission), // dwShareMode + nullptr, // lpSecurityAttributes // + creation_disposition, // dwCreationDisposition // + flags, // dwFlagsAndAttributes // + nullptr // hTemplateFile // + ); + + if (handle == INVALID_HANDLE_VALUE) { + FML_DLOG(ERROR) << "Could not open file. " << GetLastErrorMessage(); + return {}; + } + + return fml::UniqueFD{handle}; +} + +fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory, + const char* path, + bool create_if_necessary, + FilePermission permission) { + return OpenDirectory(GetAbsolutePath(base_directory, path).c_str(), + create_if_necessary, permission); +} + +fml::UniqueFD OpenDirectory(const char* path, + bool create_if_necessary, + FilePermission permission) { + if (path == nullptr || strlen(path) == 0) { + return {}; + } + + auto file_name = StringToWideString({path}); + + if (file_name.size() == 0) { + return {}; + } + + if (create_if_necessary) { + if (!::CreateDirectory(file_name.c_str(), nullptr)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) { + FML_DLOG(ERROR) << "Could not create directory. " + << GetLastErrorMessage(); + return {}; + } + } + } + + const DWORD creation_disposition = OPEN_EXISTING; + + const DWORD flags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS; + + auto handle = + CreateFile(file_name.c_str(), // lpFileName + GetDesiredAccessFlags(permission), // dwDesiredAccess + GetShareFlags(permission), // dwShareMode + nullptr, // lpSecurityAttributes // + creation_disposition, // dwCreationDisposition // + flags, // dwFlagsAndAttributes // + nullptr // hTemplateFile // + ); + + if (handle == INVALID_HANDLE_VALUE) { + FML_DLOG(ERROR) << "Could not open file. " << GetLastErrorMessage(); + return {}; + } + + return fml::UniqueFD{handle}; } fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor) { @@ -127,6 +245,8 @@ fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor) { bool IsDirectory(const fml::UniqueFD& directory) { BY_HANDLE_FILE_INFORMATION info; if (!::GetFileInformationByHandle(directory.get(), &info)) { + FML_DLOG(ERROR) << "Could not get file information. " + << GetLastErrorMessage(); return false; } return info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; @@ -139,4 +259,134 @@ bool IsFile(const std::string& path) { return S_ISREG(buf.st_mode); } +bool UnlinkDirectory(const char* path) { + if (!::RemoveDirectory(ConvertToWString(path).c_str())) { + FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. " + << GetLastErrorMessage(); + return false; + } + return true; +} + +bool UnlinkDirectory(const fml::UniqueFD& base_directory, const char* path) { + if (!::RemoveDirectory( + StringToWideString(GetAbsolutePath(base_directory, path)).c_str())) { + FML_DLOG(ERROR) << "Could not remove directory: '" << path << "'. " + << GetLastErrorMessage(); + return false; + } + return true; +} + +bool UnlinkFile(const char* path) { + if (!::DeleteFile(ConvertToWString(path).c_str())) { + FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. " + << GetLastErrorMessage(); + return false; + } + return true; +} + +bool UnlinkFile(const fml::UniqueFD& base_directory, const char* path) { + if (!::DeleteFile( + StringToWideString(GetAbsolutePath(base_directory, path)).c_str())) { + FML_DLOG(ERROR) << "Could not remove file: '" << path << "'. " + << GetLastErrorMessage(); + return false; + } + return true; +} + +bool TruncateFile(const fml::UniqueFD& file, size_t size) { + LARGE_INTEGER large_size; + large_size.QuadPart = size; + large_size.LowPart = SetFilePointer(file.get(), large_size.LowPart, + &large_size.HighPart, FILE_BEGIN); + if (large_size.LowPart == INVALID_SET_FILE_POINTER && + GetLastError() != NO_ERROR) { + FML_DLOG(ERROR) << "Could not update file size. " << GetLastErrorMessage(); + return false; + } + + if (!::SetEndOfFile(file.get())) { + FML_DLOG(ERROR) << "Could not commit file size update. " + << GetLastErrorMessage(); + return false; + } + return true; +} + +bool WriteAtomically(const fml::UniqueFD& base_directory, + const char* file_name, + const Mapping& mapping) { + if (file_name == nullptr) { + return false; + } + + auto file_path = GetAbsolutePath(base_directory, file_name); + std::stringstream stream; + stream << file_path << ".temp"; + auto temp_file_path = stream.str(); + + auto temp_file = + OpenFile(temp_file_path.c_str(), true, FilePermission::kReadWrite); + + if (!temp_file.is_valid()) { + FML_DLOG(ERROR) << "Could not create temporary file."; + return false; + } + + if (!TruncateFile(temp_file, mapping.GetSize())) { + FML_DLOG(ERROR) << "Could not truncate the file to the correct size. " + << GetLastErrorMessage(); + return false; + } + + { + FileMapping temp_file_mapping(temp_file, {FileMapping::Protection::kRead, + FileMapping::Protection::kWrite}); + if (temp_file_mapping.GetSize() != mapping.GetSize()) { + FML_DLOG(ERROR) << "Temporary file mapping size was incorrect. Is " + << temp_file_mapping.GetSize() << ". Should be " + << mapping.GetSize() << "."; + return false; + } + + if (temp_file_mapping.GetMutableMapping() == nullptr) { + FML_DLOG(ERROR) << "Temporary file does not have a mutable mapping."; + return false; + } + + ::memcpy(temp_file_mapping.GetMutableMapping(), mapping.GetMapping(), + mapping.GetSize()); + + if (!::FlushViewOfFile(temp_file_mapping.GetMutableMapping(), + mapping.GetSize())) { + FML_DLOG(ERROR) << "Could not flush file view. " << GetLastErrorMessage(); + return false; + } + + if (!::FlushFileBuffers(temp_file.get())) { + FML_DLOG(ERROR) << "Could not flush file buffers. " + << GetLastErrorMessage(); + return false; + } + + // File mapping is detroyed. + } + + temp_file.reset(); + + if (!::MoveFile(StringToWideString(temp_file_path).c_str(), + StringToWideString(file_path).c_str())) { + FML_DLOG(ERROR) + << "Could not replace temp file at correct path. File path: " + << file_path << ". Temp file path: " << temp_file_path << " " + << GetLastErrorMessage(); + return false; + } + + return true; +} + } // namespace fml diff --git a/engine/src/flutter/fml/platform/win/mapping_win.cc b/engine/src/flutter/fml/platform/win/mapping_win.cc index 0961735878..aaacdb4b8d 100644 --- a/engine/src/flutter/fml/platform/win/mapping_win.cc +++ b/engine/src/flutter/fml/platform/win/mapping_win.cc @@ -11,6 +11,7 @@ #include #include "flutter/fml/file.h" +#include "flutter/fml/platform/win/errors_win.h" #include "flutter/fml/platform/win/wstring_conversion.h" namespace fml { @@ -19,45 +20,79 @@ Mapping::Mapping() = default; Mapping::~Mapping() = default; -FileMapping::FileMapping(const std::string& path, bool executable) - : FileMapping(OpenFile(path.c_str(), - executable ? OpenPermission::kExecute - : OpenPermission::kRead, - false), - executable) {} +static bool IsWritable( + std::initializer_list protection_flags) { + for (auto protection : protection_flags) { + if (protection == FileMapping::Protection::kWrite) { + return true; + } + } + return false; +} -FileMapping::FileMapping(const fml::UniqueFD& fd, bool executable) +static bool IsExecutable( + std::initializer_list protection_flags) { + for (auto protection : protection_flags) { + if (protection == FileMapping::Protection::kExecute) { + return true; + } + } + return false; +} + +FileMapping::FileMapping(const fml::UniqueFD& fd, + std::initializer_list protections) : size_(0), mapping_(nullptr) { if (!fd.is_valid()) { return; } - if (auto size = ::GetFileSize(fd.get(), nullptr)) { - if (size > 0) { - size_ = size; - } else { - return; - } + const auto mapping_size = ::GetFileSize(fd.get(), nullptr); + + if (mapping_size == INVALID_FILE_SIZE) { + FML_DLOG(ERROR) << "Invalid file size. " << GetLastErrorMessage(); + return; } - const DWORD protect = executable ? PAGE_EXECUTE_READ : PAGE_READONLY; + DWORD protect_flags = 0; + bool read_only = !IsWritable(protections); - mapping_handle_.reset(::CreateFileMapping(fd.get(), // hFile - nullptr, // lpAttributes - protect, // flProtect - 0, // dwMaximumSizeHigh - 0, // dwMaximumSizeLow - nullptr // lpName + if (IsExecutable(protections)) { + protect_flags = PAGE_EXECUTE_READ; + } else if (read_only) { + protect_flags = PAGE_READONLY; + } else { + protect_flags = PAGE_READWRITE; + } + + mapping_handle_.reset(::CreateFileMapping(fd.get(), // hFile + nullptr, // lpAttributes + protect_flags, // flProtect + 0, // dwMaximumSizeHigh + 0, // dwMaximumSizeLow + nullptr // lpName )); if (!mapping_handle_.is_valid()) { return; } - const DWORD desired_access = executable ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ; + const DWORD desired_access = read_only ? FILE_MAP_READ : FILE_MAP_WRITE; - mapping_ = reinterpret_cast( - MapViewOfFile(mapping_handle_.get(), desired_access, 0, 0, size_)); + auto mapping = reinterpret_cast( + MapViewOfFile(mapping_handle_.get(), desired_access, 0, 0, mapping_size)); + + if (mapping == nullptr) { + FML_DLOG(ERROR) << "Could not setup file mapping. " + << GetLastErrorMessage(); + return; + } + + mapping_ = mapping; + size_ = mapping_size; + if (IsWritable(protections)) { + mutable_mapping_ = mapping_; + } } FileMapping::~FileMapping() { diff --git a/engine/src/flutter/fml/platform/win/paths_win.cc b/engine/src/flutter/fml/platform/win/paths_win.cc index 5f6604bb39..156ac70f98 100644 --- a/engine/src/flutter/fml/platform/win/paths_win.cc +++ b/engine/src/flutter/fml/platform/win/paths_win.cc @@ -90,5 +90,10 @@ std::string FromURI(const std::string& uri) { return SanitizeURIEscapedCharacters(file_path); } +fml::UniqueFD GetCachesDirectory() { + // Unsupported on this platform. + return {}; +} + } // namespace paths } // namespace fml diff --git a/engine/src/flutter/fml/platform/win/wstring_conversion.h b/engine/src/flutter/fml/platform/win/wstring_conversion.h index 0682dd8406..35908fe5a2 100644 --- a/engine/src/flutter/fml/platform/win/wstring_conversion.h +++ b/engine/src/flutter/fml/platform/win/wstring_conversion.h @@ -11,6 +11,9 @@ namespace fml { +using WideStringConvertor = + std::wstring_convert, wchar_t>; + inline std::wstring ConvertToWString(const char* path) { if (path == nullptr) { return {}; @@ -20,6 +23,16 @@ inline std::wstring ConvertToWString(const char* path) { return wchar_conv.from_bytes(path8); } +inline std::wstring StringToWideString(const std::string& str) { + WideStringConvertor converter; + return converter.from_bytes(str); +} + +inline std::string WideStringToString(const std::wstring& wstr) { + WideStringConvertor converter; + return converter.to_bytes(wstr); +} + } // namespace fml #endif // FLUTTER_FML_PLATFORM_WIN_WSTRING_CONVERSION_H_ diff --git a/engine/src/flutter/runtime/dart_snapshot.cc b/engine/src/flutter/runtime/dart_snapshot.cc index 3c8f519439..19d0821d92 100644 --- a/engine/src/flutter/runtime/dart_snapshot.cc +++ b/engine/src/flutter/runtime/dart_snapshot.cc @@ -43,7 +43,9 @@ static const char* kIsolateInstructionsSymbolSo = std::unique_ptr ResolveVMData(const Settings& settings) { if (settings.vm_snapshot_data_path.size() > 0) { if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( - settings.vm_snapshot_data_path.c_str(), false /* executable */)) { + fml::OpenFile(settings.vm_snapshot_data_path.c_str(), false, + fml::FilePermission::kRead), + {fml::FileMapping::Protection::kRead})) { return source; } } @@ -66,7 +68,9 @@ std::unique_ptr ResolveVMInstructions( const Settings& settings) { if (settings.vm_snapshot_instr_path.size() > 0) { if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( - settings.vm_snapshot_instr_path.c_str(), true /* executable */)) { + fml::OpenFile(settings.vm_snapshot_instr_path.c_str(), false, + fml::FilePermission::kRead), + {fml::FileMapping::Protection::kExecute})) { return source; } } @@ -89,8 +93,9 @@ std::unique_ptr ResolveIsolateData( const Settings& settings) { if (settings.isolate_snapshot_data_path.size() > 0) { if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( - settings.isolate_snapshot_data_path.c_str(), - false /* executable */)) { + fml::OpenFile(settings.isolate_snapshot_data_path.c_str(), false, + fml::FilePermission::kRead), + {fml::FileMapping::Protection::kRead})) { return source; } } @@ -113,8 +118,9 @@ std::unique_ptr ResolveIsolateInstructions( const Settings& settings) { if (settings.isolate_snapshot_instr_path.size() > 0) { if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( - settings.isolate_snapshot_instr_path.c_str(), - true /* executable */)) { + fml::OpenFile(settings.isolate_snapshot_instr_path.c_str(), false, + fml::FilePermission::kRead), + {fml::FileMapping::Protection::kExecute})) { return source; } } diff --git a/engine/src/flutter/runtime/dart_snapshot_buffer.cc b/engine/src/flutter/runtime/dart_snapshot_buffer.cc index 125447e59b..342613f185 100644 --- a/engine/src/flutter/runtime/dart_snapshot_buffer.cc +++ b/engine/src/flutter/runtime/dart_snapshot_buffer.cc @@ -33,8 +33,10 @@ class NativeLibrarySnapshotBuffer final : public DartSnapshotBuffer { class FileSnapshotBuffer final : public DartSnapshotBuffer { public: - FileSnapshotBuffer(const char* path, bool executable) - : mapping_(path, executable) { + FileSnapshotBuffer( + const fml::UniqueFD& fd, + std::initializer_list protection) + : mapping_(fd, protection) { if (mapping_.GetSize() > 0) { symbol_ = mapping_.GetMapping(); } @@ -75,9 +77,10 @@ DartSnapshotBuffer::CreateWithSymbolInLibrary( } std::unique_ptr -DartSnapshotBuffer::CreateWithContentsOfFile(const char* file_path, - bool executable) { - auto source = std::make_unique(file_path, executable); +DartSnapshotBuffer::CreateWithContentsOfFile( + const fml::UniqueFD& fd, + std::initializer_list protection) { + auto source = std::make_unique(fd, protection); return source->GetSnapshotPointer() == nullptr ? nullptr : std::move(source); } diff --git a/engine/src/flutter/runtime/dart_snapshot_buffer.h b/engine/src/flutter/runtime/dart_snapshot_buffer.h index fec92c6820..75fff29d7e 100644 --- a/engine/src/flutter/runtime/dart_snapshot_buffer.h +++ b/engine/src/flutter/runtime/dart_snapshot_buffer.h @@ -5,9 +5,12 @@ #ifndef FLUTTER_RUNTIME_DART_SNAPSHOT_BUFFER_H_ #define FLUTTER_RUNTIME_DART_SNAPSHOT_BUFFER_H_ +#include #include +#include "flutter/fml/file.h" #include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" #include "flutter/fml/native_library.h" namespace blink { @@ -19,8 +22,8 @@ class DartSnapshotBuffer { const char* symbol_name); static std::unique_ptr CreateWithContentsOfFile( - const char* file_path, - bool executable); + const fml::UniqueFD& fd, + std::initializer_list protection); static std::unique_ptr CreateWithUnmanagedAllocation( const uint8_t* allocation); diff --git a/engine/src/flutter/shell/common/BUILD.gn b/engine/src/flutter/shell/common/BUILD.gn index db0b6dd43e..628cd30c5f 100644 --- a/engine/src/flutter/shell/common/BUILD.gn +++ b/engine/src/flutter/shell/common/BUILD.gn @@ -67,6 +67,8 @@ source_set("common") { "io_manager.h", "isolate_configuration.cc", "isolate_configuration.h", + "persistent_cache.cc", + "persistent_cache.h", "platform_view.cc", "platform_view.h", "rasterizer.cc", diff --git a/engine/src/flutter/shell/common/io_manager.cc b/engine/src/flutter/shell/common/io_manager.cc index d75bf9a6ab..c92df1e910 100644 --- a/engine/src/flutter/shell/common/io_manager.cc +++ b/engine/src/flutter/shell/common/io_manager.cc @@ -5,6 +5,7 @@ #include "flutter/shell/common/io_manager.h" #include "flutter/fml/message_loop.h" +#include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" namespace shell { @@ -17,6 +18,8 @@ sk_sp IOManager::CreateCompatibleResourceLoadingContext( GrContextOptions options = {}; + options.fPersistentCache = PersistentCache::GetCacheForProcess(); + // There is currently a bug with doing GPU YUV to RGB conversions on the IO // thread. The necessary work isn't being flushed or synchronized with the // other threads correctly, so the textures end up blank. For now, suppress diff --git a/engine/src/flutter/shell/common/persistent_cache.cc b/engine/src/flutter/shell/common/persistent_cache.cc new file mode 100644 index 0000000000..959fb99745 --- /dev/null +++ b/engine/src/flutter/shell/common/persistent_cache.cc @@ -0,0 +1,102 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/common/persistent_cache.h" + +#include +#include + +#include "flutter/fml/base32.h" +#include "flutter/fml/file.h" +#include "flutter/fml/mapping.h" +#include "flutter/fml/paths.h" +#include "flutter/fml/trace_event.h" +#include "flutter/shell/version/version.h" + +namespace shell { + +static std::string SkKeyToFilePath(const SkData& data) { + if (data.data() == nullptr || data.size() == 0) { + return ""; + } + + fml::StringView view(reinterpret_cast(data.data()), data.size()); + + auto encode_result = fml::Base32Encode(view); + + if (!encode_result.first) { + return ""; + } + + return encode_result.second; +} + +PersistentCache* PersistentCache::GetCacheForProcess() { + static std::unique_ptr gPersistentCache; + static std::once_flag once = {}; + std::call_once(once, []() { gPersistentCache.reset(new PersistentCache()); }); + return gPersistentCache.get(); +} + +PersistentCache::PersistentCache() + : cache_directory_(CreateDirectory(fml::paths::GetCachesDirectory(), + { + "flutter_engine", // + GetFlutterEngineVersion(), // + "skia", // + GetSkiaVersion() // + }, + fml::FilePermission::kReadWrite)) { + if (!cache_directory_.is_valid()) { + FML_LOG(ERROR) << "Could not acquire the persistent cache directory. " + "Caching of GPU resources on disk is disabled."; + } +} + +PersistentCache::~PersistentCache() = default; + +// |GrContextOptions::PersistentCache| +sk_sp PersistentCache::load(const SkData& key) { + TRACE_EVENT0("flutter", "PersistentCacheLoad"); + if (!cache_directory_.is_valid()) { + return nullptr; + } + auto file_name = SkKeyToFilePath(key); + if (file_name.size() == 0) { + return nullptr; + } + auto file = fml::OpenFile(cache_directory_, file_name.c_str(), false, + fml::FilePermission::kRead); + if (!file.is_valid()) { + return nullptr; + } + auto mapping = std::make_unique(file); + if (mapping->GetSize() == 0) { + return nullptr; + } + + TRACE_EVENT0("flutter", "PersistentCacheLoadHit"); + return SkData::MakeWithCopy(mapping->GetMapping(), mapping->GetSize()); +} + +// |GrContextOptions::PersistentCache| +void PersistentCache::store(const SkData& key, const SkData& data) { + TRACE_EVENT0("flutter", "PersistentCacheStore"); + if (!cache_directory_.is_valid()) { + return; + } + + auto file_name = SkKeyToFilePath(key); + auto mapping = + std::make_unique(data.bytes(), data.size()); + + if (!fml::WriteAtomically(cache_directory_, // + file_name.c_str(), // + *mapping) // + ) { + FML_DLOG(ERROR) << "Could not write cache contents to persistent store."; + } +} + +} // namespace shell diff --git a/engine/src/flutter/shell/common/persistent_cache.h b/engine/src/flutter/shell/common/persistent_cache.h new file mode 100644 index 0000000000..2d436cc173 --- /dev/null +++ b/engine/src/flutter/shell/common/persistent_cache.h @@ -0,0 +1,38 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ +#define FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ + +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/unique_fd.h" +#include "third_party/skia/include/gpu/GrContextOptions.h" + +namespace shell { + +class PersistentCache : public GrContextOptions::PersistentCache { + public: + static PersistentCache* GetCacheForProcess(); + + ~PersistentCache() override; + + private: + fml::UniqueFD cache_directory_; + + PersistentCache(); + + // |GrContextOptions::PersistentCache| + sk_sp load(const SkData& key) override; + + // |GrContextOptions::PersistentCache| + void store(const SkData& key, const SkData& data) override; + + FML_DISALLOW_COPY_AND_ASSIGN(PersistentCache); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_PERSISTENT_CACHE_H_ diff --git a/engine/src/flutter/shell/common/run_configuration.cc b/engine/src/flutter/shell/common/run_configuration.cc index 1fba1da6b9..57784a0e08 100644 --- a/engine/src/flutter/shell/common/run_configuration.cc +++ b/engine/src/flutter/shell/common/run_configuration.cc @@ -20,8 +20,8 @@ RunConfiguration RunConfiguration::InferFromSettings( fml::Duplicate(settings.assets_dir))); asset_manager->PushBack( - std::make_unique(fml::OpenFile( - settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + std::make_unique(fml::OpenDirectory( + settings.assets_path.c_str(), false, fml::FilePermission::kRead))); return {IsolateConfiguration::InferFromSettings(settings, asset_manager), asset_manager}; diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 7d434cb055..a8b24fc026 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -873,7 +873,8 @@ bool Shell::OnServiceProtocolRunInView( fml::paths::FromURI(params.at("assetDirectory").ToString()); auto main_script_file_mapping = - std::make_unique(main_script_path, false); + std::make_unique(fml::OpenFile( + main_script_path.c_str(), false, fml::FilePermission::kRead)); auto isolate_configuration = IsolateConfiguration::CreateForKernel( std::move(main_script_file_mapping)); @@ -881,8 +882,8 @@ bool Shell::OnServiceProtocolRunInView( RunConfiguration configuration(std::move(isolate_configuration)); configuration.AddAssetResolver( - std::make_unique(fml::OpenFile( - asset_directory_path.c_str(), fml::OpenPermission::kRead, true))); + std::make_unique(fml::OpenDirectory( + asset_directory_path.c_str(), false, fml::FilePermission::kRead))); auto& allocator = response.GetAllocator(); response.SetObject(); @@ -938,8 +939,8 @@ bool Shell::OnServiceProtocolSetAssetBundlePath( auto asset_manager = fml::MakeRefCounted(); asset_manager->PushFront(std::make_unique( - fml::OpenFile(params.at("assetDirectory").ToString().c_str(), - fml::OpenPermission::kRead, true))); + fml::OpenDirectory(params.at("assetDirectory").ToString().c_str(), false, + fml::FilePermission::kRead))); if (engine_->UpdateAssetManager(std::move(asset_manager))) { response.AddMember("type", "Success", allocator); diff --git a/engine/src/flutter/shell/gpu/gpu_surface_gl.cc b/engine/src/flutter/shell/gpu/gpu_surface_gl.cc index 5a7c2a1984..f55f972649 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_gl.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_gl.cc @@ -7,6 +7,7 @@ #include "flutter/fml/arraysize.h" #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/common/persistent_cache.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" @@ -46,6 +47,9 @@ GPUSurfaceGL::GPUSurfaceGL(GPUSurfaceGLDelegate* delegate) proc_resolver_ = delegate_->GetGLProcResolver(); GrContextOptions options; + + options.fPersistentCache = PersistentCache::GetCacheForProcess(); + options.fAvoidStencilBuffers = true; // To get video playback on the widest range of devices, we limit Skia to diff --git a/engine/src/flutter/shell/platform/android/flutter_main.cc b/engine/src/flutter/shell/platform/android/flutter_main.cc index 0bcf75d76f..6311ed72a2 100644 --- a/engine/src/flutter/shell/platform/android/flutter_main.cc +++ b/engine/src/flutter/shell/platform/android/flutter_main.cc @@ -15,6 +15,7 @@ #include "flutter/fml/message_loop.h" #include "flutter/fml/paths.h" #include "flutter/fml/platform/android/jni_util.h" +#include "flutter/fml/platform/android/paths_android.h" #include "flutter/lib/ui/plugins/callback_cache.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/start_up.h" @@ -46,7 +47,8 @@ void FlutterMain::Init(JNIEnv* env, jobject context, jobjectArray jargs, jstring bundlePath, - jstring appStoragePath) { + jstring appStoragePath, + jstring engineCachesPath) { std::vector args; args.push_back("flutter"); for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) { @@ -59,8 +61,14 @@ void FlutterMain::Init(JNIEnv* env, settings.assets_path = fml::jni::JavaStringToString(env, bundlePath); // Restore the callback cache. + // TODO(chinmaygarde): Route all cache file access through FML and remove this + // setter. blink::DartCallbackCache::SetCachePath( fml::jni::JavaStringToString(env, appStoragePath)); + + fml::paths::InitializeAndroidCachesPath( + fml::jni::JavaStringToString(env, engineCachesPath)); + blink::DartCallbackCache::LoadCacheFromDisk(); if (!blink::DartVM::IsRunningPrecompiledCode()) { @@ -99,7 +107,7 @@ bool FlutterMain::Register(JNIEnv* env) { { .name = "nativeInit", .signature = "(Landroid/content/Context;[Ljava/lang/String;Ljava/" - "lang/String;Ljava/lang/String;)V", + "lang/String;Ljava/lang/String;Ljava/lang/String;)V", .fnPtr = reinterpret_cast(&Init), }, { diff --git a/engine/src/flutter/shell/platform/android/flutter_main.h b/engine/src/flutter/shell/platform/android/flutter_main.h index 6ad122f999..7d738a15b9 100644 --- a/engine/src/flutter/shell/platform/android/flutter_main.h +++ b/engine/src/flutter/shell/platform/android/flutter_main.h @@ -32,7 +32,8 @@ class FlutterMain { jobject context, jobjectArray jargs, jstring bundlePath, - jstring appRootPath); + jstring appRootPath, + jstring engineCachesPath); FML_DISALLOW_COPY_AND_ASSIGN(FlutterMain); }; diff --git a/engine/src/flutter/shell/platform/android/io/flutter/view/FlutterMain.java b/engine/src/flutter/shell/platform/android/io/flutter/view/FlutterMain.java index 198bcade69..cb4bd64e48 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/view/FlutterMain.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/view/FlutterMain.java @@ -214,8 +214,9 @@ public class FlutterMain { String appBundlePath = findAppBundlePath(applicationContext); String appStoragePath = PathUtils.getFilesDir(applicationContext); + String engineCachesPath = PathUtils.getCacheDirectory(applicationContext); nativeInit(applicationContext, shellArgs.toArray(new String[0]), - appBundlePath, appStoragePath); + appBundlePath, appStoragePath, engineCachesPath); sInitialized = true; } catch (Exception e) { @@ -224,7 +225,7 @@ public class FlutterMain { } } - private static native void nativeInit(Context context, String[] args, String bundlePath, String appStoragePath); + private static native void nativeInit(Context context, String[] args, String bundlePath, String appStoragePath, String engineCachesPath); private static native void nativeRecordStartTimestamp(long initTimeMillis); /** diff --git a/engine/src/flutter/shell/platform/android/platform_view_android_jni.cc b/engine/src/flutter/shell/platform/android/platform_view_android_jni.cc index d93f935714..e84e68737b 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android_jni.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android_jni.cc @@ -252,8 +252,9 @@ static void RunBundleAndSnapshotFromLibrary(JNIEnv* env, asset_manager->PushBack( std::make_unique(bundlepath)); } else { - asset_manager->PushBack(std::make_unique( - fml::OpenFile(bundlepath.c_str(), fml::OpenPermission::kRead, true))); + asset_manager->PushBack( + std::make_unique(fml::OpenDirectory( + bundlepath.c_str(), false, fml::FilePermission::kRead))); } // Use the last path component of the bundle path to determine the @@ -273,8 +274,9 @@ static void RunBundleAndSnapshotFromLibrary(JNIEnv* env, const auto defaultpath = fml::jni::JavaStringToString(env, jdefaultPath); if (defaultpath.size() > 0) { - asset_manager->PushBack(std::make_unique( - fml::OpenFile(defaultpath.c_str(), fml::OpenPermission::kRead, true))); + asset_manager->PushBack( + std::make_unique(fml::OpenDirectory( + defaultpath.c_str(), false, fml::FilePermission::kRead))); } auto isolate_configuration = CreateIsolateConfiguration(*asset_manager); diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index d0a87703b9..cecb6cd4e9 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -370,8 +370,8 @@ FlutterResult FlutterEngineRun(size_t version, fml::Duplicate(settings.assets_dir))); run_configuration.AddAssetResolver( - std::make_unique(fml::OpenFile( - settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + std::make_unique(fml::OpenDirectory( + settings.assets_path.c_str(), false, fml::FilePermission::kRead))); if (!embedder_engine->Run(std::move(run_configuration))) { return kInvalidArguments; diff --git a/engine/src/flutter/shell/testing/tester_main.cc b/engine/src/flutter/shell/testing/tester_main.cc index d5c628da77..83b4e1939e 100644 --- a/engine/src/flutter/shell/testing/tester_main.cc +++ b/engine/src/flutter/shell/testing/tester_main.cc @@ -129,8 +129,13 @@ int RunTester(const blink::Settings& settings, bool run_forever) { return EXIT_FAILURE; } + std::initializer_list protection = { + fml::FileMapping::Protection::kRead}; auto main_dart_file_mapping = std::make_unique( - fml::paths::AbsolutePath(settings.main_dart_file_path), false); + fml::OpenFile( + fml::paths::AbsolutePath(settings.main_dart_file_path).c_str(), false, + fml::FilePermission::kRead), + protection); auto isolate_configuration = IsolateConfiguration::CreateForKernel(std::move(main_dart_file_mapping)); @@ -144,8 +149,8 @@ int RunTester(const blink::Settings& settings, bool run_forever) { asset_manager->PushBack(std::make_unique( fml::Duplicate(settings.assets_dir))); asset_manager->PushBack( - std::make_unique(fml::OpenFile( - settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + std::make_unique(fml::OpenDirectory( + settings.assets_path.c_str(), false, fml::FilePermission::kRead))); RunConfiguration run_configuration(std::move(isolate_configuration), std::move(asset_manager));