diff options
47 files changed, 1118 insertions, 283 deletions
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index d35099a837..86df596c64 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -96,6 +96,17 @@ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk LOCAL_CLANG := true include $(BUILD_EXECUTABLE) +# OTA slot script + +include $(CLEAR_VARS) +LOCAL_MODULE:= otapreopt_slot +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_SRC_FILES := otapreopt_slot.sh +LOCAL_INIT_RC := otapreopt.rc + +include $(BUILD_PREBUILT) + # OTA postinstall script include $(CLEAR_VARS) @@ -104,9 +115,9 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_SRC_FILES := otapreopt_script.sh -# Let this depend on otapreopt and the chroot tool, so we just have to mention one in a -# configuration. -LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot +# Let this depend on otapreopt, the chroot tool and the slot script, so we just have to mention one +# in a configuration. +LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot otapreopt_slot include $(BUILD_PREBUILT) diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp index 89994414e2..f4044b28a1 100644 --- a/cmds/installd/commands.cpp +++ b/cmds/installd/commands.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <inttypes.h> +#include <regex> #include <stdlib.h> #include <sys/capability.h> #include <sys/file.h> @@ -43,6 +44,7 @@ #include <globals.h> #include <installd_deps.h> +#include <otapreopt_utils.h> #include <utils.h> #ifndef LOG_TAG @@ -58,6 +60,26 @@ namespace installd { static constexpr const char* kCpPath = "/system/bin/cp"; static constexpr const char* kXattrDefault = "user.default"; +static constexpr const char* PKG_LIB_POSTFIX = "/lib"; +static constexpr const char* CACHE_DIR_POSTFIX = "/cache"; +static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache"; + +static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/"; +static constexpr const char* IDMAP_SUFFIX = "@idmap"; + +// NOTE: keep in sync with StorageManager +static constexpr int FLAG_STORAGE_DE = 1 << 0; +static constexpr int FLAG_STORAGE_CE = 1 << 1; + +// NOTE: keep in sync with Installer +static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; +static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; + +/* dexopt needed flags matching those in dalvik.system.DexFile */ +static constexpr int DEXOPT_DEX2OAT_NEEDED = 1; +static constexpr int DEXOPT_PATCHOAT_NEEDED = 2; +static constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3; + #define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M typedef int fd_t; @@ -755,6 +777,16 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ sprintf(image_format_arg, "--image-format=%s", app_image_format); } + char dex2oat_large_app_threshold[kPropertyValueMax]; + bool have_dex2oat_large_app_threshold = + get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0; + char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax]; + if (have_dex2oat_large_app_threshold) { + sprintf(dex2oat_large_app_threshold_arg, + "--very-large-app-threshold=%s", + dex2oat_large_app_threshold); + } + static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; static const char* RUNTIME_ARG = "--runtime-arg"; @@ -855,7 +887,8 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ + (have_app_image_format ? 1 : 0) + dex2oat_flags_count + (profile_fd == -1 ? 0 : 1) - + (shared_libraries != nullptr ? 4 : 0)]; + + (shared_libraries != nullptr ? 4 : 0) + + (have_dex2oat_large_app_threshold ? 1 : 0)]; int i = 0; argv[i++] = DEX2OAT_BIN; argv[i++] = zip_fd_arg; @@ -898,6 +931,9 @@ static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_ if (have_app_image_format) { argv[i++] = image_format_arg; } + if (have_dex2oat_large_app_threshold) { + argv[i++] = dex2oat_large_app_threshold_arg; + } if (dex2oat_flags_count) { i += split(dex2oat_flags, argv + i); } @@ -1413,7 +1449,7 @@ static const char* parse_null(const char* arg) { } } -int dexopt(const char* params[DEXOPT_PARAM_COUNT]) { +int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) { return dexopt(params[0], // apk_path atoi(params[1]), // uid params[2], // pkgname @@ -2080,6 +2116,23 @@ int move_ab(const char* apk_path, const char* instruction_set, const char* oat_d LOG(ERROR) << "Cannot move_ab with null input"; return -1; } + + // Get the current slot suffix. No suffix, no A/B. + std::string slot_suffix; + { + char buf[kPropertyValueMax]; + if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) { + return -1; + } + slot_suffix = buf; + + if (!ValidateTargetSlotSuffix(slot_suffix)) { + LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix; + return -1; + } + } + + // Validate other inputs. if (validate_apk_path(apk_path) != 0) { LOG(ERROR) << "invalid apk_path " << apk_path; return -1; @@ -2095,9 +2148,11 @@ int move_ab(const char* apk_path, const char* instruction_set, const char* oat_d } const std::string a_image_path = create_image_filename(a_path); - // B path = A path + ".b" - const std::string b_path = StringPrintf("%s.b", a_path); - const std::string b_image_path = StringPrintf("%s.b", a_image_path.c_str()); + // B path = A path + slot suffix. + const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str()); + const std::string b_image_path = StringPrintf("%s.%s", + a_image_path.c_str(), + slot_suffix.c_str()); bool oat_success = move_ab_path(b_path, a_path); bool success; diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h index c0c39c5e67..e990f1b695 100644 --- a/cmds/installd/commands.h +++ b/cmds/installd/commands.h @@ -28,6 +28,8 @@ namespace android { namespace installd { +static constexpr size_t DEXOPT_PARAM_COUNT = 10U; + int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags, appid_t appid, const char* seinfo, int target_sdk_version); int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags, @@ -69,7 +71,7 @@ int dexopt(const char *apk_path, static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size"); // Helper for the above, converting arguments. -int dexopt(const char* params[DEXOPT_PARAM_COUNT]); +int dexopt(const char* const params[DEXOPT_PARAM_COUNT]); int mark_boot_complete(const char *instruction_set); int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId); diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp index 6a67e29396..93e1ce54af 100644 --- a/cmds/installd/globals.cpp +++ b/cmds/installd/globals.cpp @@ -30,6 +30,22 @@ namespace android { namespace installd { +static constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA + +static constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA + +static constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under + // ANDROID_DATA + +static constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA + +static constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA + +static constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA + +static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under + // ANDROID_DATA + /* Directory records that are used in execution of commands. */ dir_rec_t android_app_dir; dir_rec_t android_app_ephemeral_dir; @@ -77,7 +93,7 @@ bool init_globals_from_data_and_root(const char* data, const char* root) { // Get the android ephemeral app directory. if (copy_and_append(&android_app_ephemeral_dir, &android_data_dir, EPHEMERAL_APP_SUBDIR) < 0) { - return -1; + return false; } // Get the android app native library directory. @@ -86,7 +102,7 @@ bool init_globals_from_data_and_root(const char* data, const char* root) { } // Get the sd-card ASEC mount point. - if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) { + if (get_path_from_env(&android_asec_dir, ASEC_MOUNTPOINT_ENV_NAME) < 0) { return false; } diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h index 3e523460ad..c90beec49c 100644 --- a/cmds/installd/globals.h +++ b/cmds/installd/globals.h @@ -23,6 +23,11 @@ namespace android { namespace installd { +/* constants */ + +// Name of the environment variable that contains the asec mountpoint. +static constexpr const char* ASEC_MOUNTPOINT_ENV_NAME = "ASEC_MOUNTPOINT"; + /* data structures */ struct dir_rec_t { diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 9d2f71bc75..facbc724ec 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp @@ -557,7 +557,7 @@ done: return 0; } -bool initialize_globals() { +static bool initialize_globals() { const char* data_path = getenv("ANDROID_DATA"); if (data_path == nullptr) { ALOGE("Could not find ANDROID_DATA"); diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h index 823b8ee671..41732cce4f 100644 --- a/cmds/installd/installd_constants.h +++ b/cmds/installd/installd_constants.h @@ -21,58 +21,19 @@ namespace android { namespace installd { -constexpr size_t DEXOPT_PARAM_COUNT = 10U; - /* elements combined with a valid package name to form paths */ constexpr const char* PRIMARY_USER_PREFIX = "data/"; constexpr const char* SECONDARY_USER_PREFIX = "user/"; -constexpr const char* PKG_DIR_POSTFIX = ""; - -constexpr const char* PKG_LIB_POSTFIX = "/lib"; - -constexpr const char* CACHE_DIR_POSTFIX = "/cache"; -constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache"; - -constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA -constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA -constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under ANDROID_DATA - -constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA - -constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA - -constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA - -/* other handy constants */ - -constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under ANDROID_DATA - // This is used as a string literal, can't be constants. TODO: std::string... #define DALVIK_CACHE "dalvik-cache" constexpr const char* DALVIK_CACHE_POSTFIX = "/classes.dex"; constexpr const char* DALVIK_CACHE_POSTFIX2 = "@classes.dex"; -constexpr const char* IDMAP_PREFIX = "/data/resource-cache/"; -constexpr const char* IDMAP_SUFFIX = "@idmap"; - constexpr size_t PKG_NAME_MAX = 128u; /* largest allowed package name */ constexpr size_t PKG_PATH_MAX = 256u; /* max size of any path we use */ -// NOTE: keep in sync with StorageManager -constexpr int FLAG_STORAGE_DE = 1 << 0; -constexpr int FLAG_STORAGE_CE = 1 << 1; - -// NOTE: keep in sync with Installer -constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; -constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; - -/* dexopt needed flags matching those in dalvik.system.DexFile */ -constexpr int DEXOPT_DEX2OAT_NEEDED = 1; -constexpr int DEXOPT_PATCHOAT_NEEDED = 2; -constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3; - /**************************************************************************** * IMPORTANT: These values are passed from Java code. Keep them in sync with * frameworks/base/services/core/java/com/android/server/pm/Installer.java diff --git a/cmds/installd/installd_deps.h b/cmds/installd/installd_deps.h index 5ff46e694f..509317803e 100644 --- a/cmds/installd/installd_deps.h +++ b/cmds/installd/installd_deps.h @@ -57,9 +57,6 @@ extern bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set); -// Initialize globals. May be implemented with the helper in globals.h. -extern bool initialize_globals(); - } // namespace installd } // namespace android diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index e1cfc9d999..5fa972a4de 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -40,6 +40,7 @@ #include <file_parsing.h> #include <globals.h> #include <installd_deps.h> // Need to fill in requirements of commands. +#include <otapreopt_utils.h> #include <system_properties.h> #include <utils.h> @@ -60,11 +61,6 @@ using android::base::StringPrintf; namespace android { namespace installd { -static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; -static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; -static constexpr const char* kOTARootDirectory = "/system-b"; -static constexpr size_t kISAIndex = 3; - template<typename T> static constexpr T RoundDown(T x, typename std::decay<T>::type n) { return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n); @@ -77,8 +73,6 @@ static constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) { class OTAPreoptService { public: - static constexpr const char* kOTADataDirectory = "/data/ota"; - // Main driver. Performs the following steps. // // 1) Parse options (read system properties etc from B partition). @@ -91,26 +85,31 @@ class OTAPreoptService { // // 5) Run update. int Main(int argc, char** argv) { + if (!ReadArguments(argc, argv)) { + LOG(ERROR) << "Failed reading command line."; + return 1; + } + if (!ReadSystemProperties()) { LOG(ERROR)<< "Failed reading system properties."; - return 1; + return 2; } if (!ReadEnvironment()) { LOG(ERROR) << "Failed reading environment properties."; - return 2; + return 3; } - if (!ReadPackage(argc, argv)) { - LOG(ERROR) << "Failed reading command line file."; - return 3; + if (!CheckAndInitializeInstalldGlobals()) { + LOG(ERROR) << "Failed initializing globals."; + return 4; } PrepareEnvironment(); - if (!PrepareBootImage()) { + if (!PrepareBootImage(/* force */ false)) { LOG(ERROR) << "Failed preparing boot image."; - return 4; + return 5; } int dexopt_retcode = RunPreopt(); @@ -118,7 +117,7 @@ class OTAPreoptService { return dexopt_retcode; } - int GetProperty(const char* key, char* value, const char* default_value) { + int GetProperty(const char* key, char* value, const char* default_value) const { const std::string* prop_value = system_properties_.GetProperty(key); if (prop_value == nullptr) { if (default_value == nullptr) { @@ -135,7 +134,16 @@ class OTAPreoptService { return static_cast<int>(size); } + std::string GetOTADataDirectory() const { + return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), target_slot_.c_str()); + } + + const std::string& GetTargetSlot() const { + return target_slot_; + } + private: + bool ReadSystemProperties() { static constexpr const char* kPropertyFiles[] = { "/default.prop", "/system/build.prop" @@ -177,29 +185,106 @@ private: return false; } - // Check that we found important properties. - constexpr const char* kRequiredProperties[] = { - kBootClassPathPropertyName, kAndroidRootPathPropertyName - }; - for (size_t i = 0; i < arraysize(kRequiredProperties); ++i) { - if (system_properties_.GetProperty(kRequiredProperties[i]) == nullptr) { - return false; - } + if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) { + return false; + } + android_data_ = *system_properties_.GetProperty(kAndroidDataPathPropertyName); + + if (system_properties_.GetProperty(kAndroidRootPathPropertyName) == nullptr) { + return false; + } + android_root_ = *system_properties_.GetProperty(kAndroidRootPathPropertyName); + + if (system_properties_.GetProperty(kBootClassPathPropertyName) == nullptr) { + return false; + } + boot_classpath_ = *system_properties_.GetProperty(kBootClassPathPropertyName); + + if (system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) == nullptr) { + return false; } + asec_mountpoint_ = *system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME); return true; } - bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) { - size_t index = 0; + const std::string& GetAndroidData() const { + return android_data_; + } + + const std::string& GetAndroidRoot() const { + return android_root_; + } + + const std::string GetOtaDirectoryPrefix() const { + return GetAndroidData() + "/ota"; + } + + bool CheckAndInitializeInstalldGlobals() { + // init_globals_from_data_and_root requires "ASEC_MOUNTPOINT" in the environment. We + // do not use any datapath that includes this, but we'll still have to set it. + CHECK(system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) != nullptr); + int result = setenv(ASEC_MOUNTPOINT_ENV_NAME, asec_mountpoint_.c_str(), 0); + if (result != 0) { + LOG(ERROR) << "Could not set ASEC_MOUNTPOINT environment variable"; + return false; + } + + if (!init_globals_from_data_and_root(GetAndroidData().c_str(), GetAndroidRoot().c_str())) { + LOG(ERROR) << "Could not initialize globals; exiting."; + return false; + } + + // This is different from the normal installd. We only do the base + // directory, the rest will be created on demand when each app is compiled. + if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) { + LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix(); + return false; + } + + return true; + } + + bool ReadArguments(int argc ATTRIBUTE_UNUSED, char** argv) { + // Expected command line: + // target-slot dexopt {DEXOPT_PARAMETERS} + // The DEXOPT_PARAMETERS are passed on to dexopt(), so we expect DEXOPT_PARAM_COUNT + // of them. We store them in package_parameters_ (size checks are done when + // parsing the special parameters and when copying into package_parameters_. + static_assert(DEXOPT_PARAM_COUNT == ARRAY_SIZE(package_parameters_), "Unexpected dexopt param count"); + + const char* target_slot_arg = argv[1]; + if (target_slot_arg == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + // Sanitize value. Only allow (a-zA-Z0-9_)+. + target_slot_ = target_slot_arg; + if (!ValidateTargetSlotSuffix(target_slot_)) { + LOG(ERROR) << "Target slot suffix not legal: " << target_slot_; + return false; + } + + // Check for "dexopt" next. + if (argv[2] == nullptr) { + LOG(ERROR) << "Missing parameters"; + return false; + } + if (std::string("dexopt").compare(argv[2]) != 0) { + LOG(ERROR) << "Second parameter not dexopt: " << argv[2]; + return false; + } + + // Copy the rest into package_parameters_, but be careful about over- and underflow. + size_t index = 0; while (index < DEXOPT_PARAM_COUNT && - argv[index + 1] != nullptr) { - package_parameters_[index] = argv[index + 1]; + argv[index + 3] != nullptr) { + package_parameters_[index] = argv[index + 3]; index++; } - if (index != ARRAY_SIZE(package_parameters_) || argv[index + 1] != nullptr) { + if (index != ARRAY_SIZE(package_parameters_) || argv[index + 3] != nullptr) { LOG(ERROR) << "Wrong number of parameters"; return false; } @@ -208,15 +293,9 @@ private: } void PrepareEnvironment() { - CHECK(system_properties_.GetProperty(kBootClassPathPropertyName) != nullptr); - const std::string& boot_cp = - *system_properties_.GetProperty(kBootClassPathPropertyName); - environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_cp.c_str())); - environ_.push_back(StringPrintf("ANDROID_DATA=%s", kOTADataDirectory)); - CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr); - const std::string& android_root = - *system_properties_.GetProperty(kAndroidRootPathPropertyName); - environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root.c_str())); + environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str())); + environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str())); + environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str())); for (const std::string& e : environ_) { putenv(const_cast<char*>(e.c_str())); @@ -225,7 +304,7 @@ private: // Ensure that we have the right boot image. The first time any app is // compiled, we'll try to generate it. - bool PrepareBootImage() { + bool PrepareBootImage(bool force) const { if (package_parameters_[kISAIndex] == nullptr) { LOG(ERROR) << "Instruction set missing."; return false; @@ -233,44 +312,114 @@ private: const char* isa = package_parameters_[kISAIndex]; // Check whether the file exists where expected. - std::string dalvik_cache = std::string(kOTADataDirectory) + "/" + DALVIK_CACHE; + std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE; std::string isa_path = dalvik_cache + "/" + isa; std::string art_path = isa_path + "/system@framework@boot.art"; std::string oat_path = isa_path + "/system@framework@boot.oat"; - if (access(art_path.c_str(), F_OK) == 0 && - access(oat_path.c_str(), F_OK) == 0) { - // Files exist, assume everything is alright. - return true; + bool cleared = false; + if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) { + // Files exist, assume everything is alright if not forced. Otherwise clean up. + if (!force) { + return true; + } + ClearDirectory(isa_path); + cleared = true; } + // Reset umask in otapreopt, so that we control the the access for the files we create. + umask(0); + // Create the directories, if necessary. if (access(dalvik_cache.c_str(), F_OK) != 0) { - if (mkdir(dalvik_cache.c_str(), 0711) != 0) { - PLOG(ERROR) << "Could not create dalvik-cache dir"; + if (!CreatePath(dalvik_cache)) { + PLOG(ERROR) << "Could not create dalvik-cache dir " << dalvik_cache; return false; } } if (access(isa_path.c_str(), F_OK) != 0) { - if (mkdir(isa_path.c_str(), 0711) != 0) { + if (!CreatePath(isa_path)) { PLOG(ERROR) << "Could not create dalvik-cache isa dir"; return false; } } // Prepare to create. - // TODO: Delete files, just for a blank slate. - const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName); + if (!cleared) { + ClearDirectory(isa_path); + } std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa); if (access(preopted_boot_art_path.c_str(), F_OK) == 0) { return PatchoatBootImage(art_path, isa); } else { // No preopted boot image. Try to compile. - return Dex2oatBootImage(boot_cp, art_path, oat_path, isa); + return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa); } } - bool PatchoatBootImage(const std::string& art_path, const char* isa) { + static bool CreatePath(const std::string& path) { + // Create the given path. Use string processing instead of dirname, as dirname's need for + // a writable char buffer is painful. + + // First, try to use the full path. + if (mkdir(path.c_str(), 0711) == 0) { + return true; + } + if (errno != ENOENT) { + PLOG(ERROR) << "Could not create path " << path; + return false; + } + + // Now find the parent and try that first. + size_t last_slash = path.find_last_of('/'); + if (last_slash == std::string::npos || last_slash == 0) { + PLOG(ERROR) << "Could not create " << path; + return false; + } + + if (!CreatePath(path.substr(0, last_slash))) { + return false; + } + + if (mkdir(path.c_str(), 0711) == 0) { + return true; + } + PLOG(ERROR) << "Could not create " << path; + return false; + } + + static void ClearDirectory(const std::string& dir) { + DIR* c_dir = opendir(dir.c_str()); + if (c_dir == nullptr) { + PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents"; + return; + } + + for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) { + const char* name = de->d_name; + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue; + } + // We only want to delete regular files and symbolic links. + std::string file = StringPrintf("%s/%s", dir.c_str(), name); + if (de->d_type != DT_REG && de->d_type != DT_LNK) { + LOG(WARNING) << "Unexpected file " + << file + << " of type " + << std::hex + << de->d_type + << " encountered."; + } else { + // Try to unlink the file. + if (unlink(file.c_str()) != 0) { + PLOG(ERROR) << "Unable to unlink " << file; + } + } + } + CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory."; + } + + bool PatchoatBootImage(const std::string& art_path, const char* isa) const { // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. std::vector<std::string> cmd; @@ -296,7 +445,7 @@ private: bool Dex2oatBootImage(const std::string& boot_cp, const std::string& art_path, const std::string& oat_path, - const char* isa) { + const char* isa) const { // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. std::vector<std::string> cmd; cmd.push_back("/system/bin/dex2oat"); @@ -362,9 +511,7 @@ private: return (strcmp(arg, "!") == 0) ? nullptr : arg; } - int RunPreopt() { - // Run the preopt. - // + bool ShouldSkipPreopt() const { // There's one thing we have to be careful about: we may/will be asked to compile an app // living in the system image. This may be a valid request - if the app wasn't compiled, // e.g., if the system image wasn't large enough to include preopted files. However, the @@ -391,9 +538,7 @@ private: constexpr size_t kApkPathIndex = 0; CHECK_GT(DEXOPT_PARAM_COUNT, kApkPathIndex); CHECK(package_parameters_[kApkPathIndex] != nullptr); - CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr); - if (StartsWith(package_parameters_[kApkPathIndex], - system_properties_.GetProperty(kAndroidRootPathPropertyName)->c_str())) { + if (StartsWith(package_parameters_[kApkPathIndex], android_root_.c_str())) { const char* last_slash = strrchr(package_parameters_[kApkPathIndex], '/'); if (last_slash != nullptr) { std::string path(package_parameters_[kApkPathIndex], @@ -401,11 +546,45 @@ private: CHECK(EndsWith(path, "/")); path = path + "oat"; if (access(path.c_str(), F_OK) == 0) { - return 0; + return true; } } } + // Another issue is unavailability of files in the new system. If the partition + // layout changes, otapreopt_chroot may not know about this. Then files from that + // partition will not be available and fail to build. This is problematic, as + // this tool will wipe the OTA artifact cache and try again (for robustness after + // a failed OTA with remaining cache artifacts). + if (access(package_parameters_[kApkPathIndex], F_OK) != 0) { + LOG(WARNING) << "Skipping preopt of non-existing package " + << package_parameters_[kApkPathIndex]; + return true; + } + + return false; + } + + int RunPreopt() { + if (ShouldSkipPreopt()) { + return 0; + } + + int dexopt_result = dexopt(package_parameters_); + if (dexopt_result == 0) { + return 0; + } + + // If the dexopt failed, we may have a stale boot image from a previous OTA run. + // Try to delete and retry. + + if (!PrepareBootImage(/* force */ true)) { + LOG(ERROR) << "Forced boot image creating failed. Original error return was " + << dexopt_result; + return dexopt_result; + } + + LOG(WARNING) << "Original dexopt failed, re-trying after boot image was regenerated."; return dexopt(package_parameters_); } @@ -414,7 +593,7 @@ private: //////////////////////////////////// // Wrapper on fork/execv to run a command in a subprocess. - bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) { + static bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) { const std::string command_line = Join(arg_vector, ' '); CHECK_GE(arg_vector.size(), 1U) << command_line; @@ -504,9 +683,8 @@ private: void AddCompilerOptionFromSystemProperty(const char* system_property, const char* prefix, bool runtime, - std::vector<std::string>& out) { - const std::string* value = - system_properties_.GetProperty(system_property); + std::vector<std::string>& out) const { + const std::string* value = system_properties_.GetProperty(system_property); if (value != nullptr) { if (runtime) { out.push_back("--runtime-arg"); @@ -519,10 +697,24 @@ private: } } + static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; + static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; + static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA"; + // The index of the instruction-set string inside the package parameters. Needed for + // some special-casing that requires knowledge of the instruction-set. + static constexpr size_t kISAIndex = 3; + // Stores the system properties read out of the B partition. We need to use these properties // to compile, instead of the A properties we could get from init/get_property. SystemProperties system_properties_; + // Some select properties that are always needed. + std::string target_slot_; + std::string android_root_; + std::string android_data_; + std::string boot_classpath_; + std::string asec_mountpoint_; + const char* package_parameters_[DEXOPT_PARAM_COUNT]; // Store environment values we need to set. @@ -563,8 +755,13 @@ bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, std::string file_name(file_name_start, file_name_len); // <apk_parent_dir>/oat/<isa>/<file_name>.odex.b - snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex.b", oat_dir, instruction_set, - file_name.c_str()); + snprintf(path, + PKG_PATH_MAX, + "%s/%s/%s.odex.%s", + oat_dir, + instruction_set, + file_name.c_str(), + gOps.GetTargetSlot().c_str()); return true; } @@ -576,11 +773,6 @@ bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, */ bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, const char *instruction_set) { - if (StringPrintf("%soat/%s/odex.b", apk_path, instruction_set).length() + 1 > PKG_PATH_MAX) { - ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path); - return false; - } - const char *path_end = strrchr(apk_path, '/'); if (path_end == nullptr) { ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path); @@ -596,11 +788,15 @@ bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path, } std::string name_component(name_begin, extension_start - name_begin); - std::string new_path = StringPrintf("%s/oat/%s/%s.odex.b", + std::string new_path = StringPrintf("%s/oat/%s/%s.odex.%s", path_component.c_str(), instruction_set, - name_component.c_str()); - CHECK_LT(new_path.length(), PKG_PATH_MAX); + name_component.c_str(), + gOps.GetTargetSlot().c_str()); + if (new_path.length() >= PKG_PATH_MAX) { + LOG(ERROR) << "apk_path of " << apk_path << " is too long: " << new_path; + return false; + } strcpy(path, new_path.c_str()); return true; } @@ -623,7 +819,7 @@ bool create_cache_path(char path[PKG_PATH_MAX], std::replace(from_src.begin(), from_src.end(), '/', '@'); std::string assembled_path = StringPrintf("%s/%s/%s/%s%s", - OTAPreoptService::kOTADataDirectory, + gOps.GetOTADataDirectory().c_str(), DALVIK_CACHE, instruction_set, from_src.c_str(), @@ -637,27 +833,6 @@ bool create_cache_path(char path[PKG_PATH_MAX], return true; } -bool initialize_globals() { - const char* data_path = getenv("ANDROID_DATA"); - if (data_path == nullptr) { - ALOGE("Could not find ANDROID_DATA"); - return false; - } - return init_globals_from_data_and_root(data_path, kOTARootDirectory); -} - -static bool initialize_directories() { - // This is different from the normal installd. We only do the base - // directory, the rest will be created on demand when each app is compiled. - mode_t old_umask = umask(0); - LOG(INFO) << "Old umask: " << old_umask; - if (access(OTAPreoptService::kOTADataDirectory, R_OK) < 0) { - ALOGE("Could not access %s\n", OTAPreoptService::kOTADataDirectory); - return false; - } - return true; -} - static int log_callback(int type, const char *fmt, ...) { va_list ap; int priority; @@ -685,8 +860,6 @@ static int otapreopt_main(const int argc, char *argv[]) { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(argv); - ALOGI("otapreopt firing up\n"); - if (argc < 2) { ALOGE("Expecting parameters"); exit(1); @@ -696,16 +869,6 @@ static int otapreopt_main(const int argc, char *argv[]) { cb.func_log = log_callback; selinux_set_callback(SELINUX_CB_LOG, cb); - if (!initialize_globals()) { - ALOGE("Could not initialize globals; exiting.\n"); - exit(1); - } - - if (!initialize_directories()) { - ALOGE("Could not create directories; exiting.\n"); - exit(1); - } - if (selinux_enabled && selinux_status_open(true) < 0) { ALOGE("Could not open selinux status; exiting.\n"); exit(1); diff --git a/cmds/installd/otapreopt.rc b/cmds/installd/otapreopt.rc new file mode 100644 index 0000000000..059ae752e7 --- /dev/null +++ b/cmds/installd/otapreopt.rc @@ -0,0 +1,8 @@ +# When /data is available, look for A/B artifacts for the current slot and move them +# into the dalvik-cache (relabeling them). +on post-fs-data + exec - root -- /system/bin/otapreopt_slot + # The dalvik-cache was not moved itself, so as to restrict the rights of otapreopt_slot. + # But now the relabeling is annoying as there is no force option available here. So + # explicitly list all the ISAs we know. + restorecon_recursive /data/dalvik-cache/arm /data/dalvik-cache/arm64 /data/dalvik-cache/mips /data/dalvik-cache/mips64 /data/dalvik-cache/x86 /data/dalvik-cache/x86_64 diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index be0ff2e140..5ea89e6842 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -14,15 +14,19 @@ ** limitations under the License. */ +#include <fcntl.h> #include <linux/unistd.h> #include <sys/mount.h> #include <sys/wait.h> +#include <sstream> + #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/stringprintf.h> -#include <installd_constants.h> +#include <commands.h> +#include <otapreopt_utils.h> #ifndef LOG_TAG #define LOG_TAG "otapreopt" @@ -33,7 +37,37 @@ using android::base::StringPrintf; namespace android { namespace installd { +static void CloseDescriptor(int fd) { + if (fd >= 0) { + int result = close(fd); + UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor + // that we do *not* want. + } +} + +static void CloseDescriptor(const char* descriptor_string) { + int fd = -1; + std::istringstream stream(descriptor_string); + stream >> fd; + if (!stream.fail()) { + CloseDescriptor(fd); + } +} + +// Entry for otapreopt_chroot. Expected parameters are: +// [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] +// The file descriptor denoted by status-fd will be closed. The rest of the parameters will +// be passed on to otapreopt in the chroot. static int otapreopt_chroot(const int argc, char **arg) { + // Close all file descriptors. They are coming from the caller, we do not want to pass them + // on across our fork/exec into a different domain. + // 1) Default descriptors. + CloseDescriptor(STDIN_FILENO); + CloseDescriptor(STDOUT_FILENO); + CloseDescriptor(STDERR_FILENO); + // 2) The status channel. + CloseDescriptor(arg[1]); + // We need to run the otapreopt tool from the postinstall partition. As such, set up a // mount namespace and change root. @@ -61,6 +95,28 @@ static int otapreopt_chroot(const int argc, char **arg) { } } + // Try to mount the vendor partition. update_engine doesn't do this for us, but we + // want it for vendor APKs. + // Notes: + // 1) We pretty much guess a name here and hope to find the partition by name. + // It is just as complicated and brittle to scan /proc/mounts. But this requires + // validating the target-slot so as not to try to mount some totally random path. + // 2) We're in a mount namespace here, so when we die, this will be cleaned up. + // 3) Ignore errors. Printing anything at this stage will open a file descriptor + // for logging. + if (!ValidateTargetSlotSuffix(arg[2])) { + LOG(ERROR) << "Target slot suffix not legal: " << arg[2]; + exit(207); + } + std::string vendor_partition = StringPrintf("/dev/block/bootdevice/by-name/vendor%s", + arg[2]); + int vendor_result = mount(vendor_partition.c_str(), + "/postinstall/vendor", + "ext4", + MS_RDONLY, + /* data */ nullptr); + UNUSED(vendor_result); + // Chdir into /postinstall. if (chdir("/postinstall") != 0) { PLOG(ERROR) << "Unable to chdir into /postinstall."; @@ -80,13 +136,42 @@ static int otapreopt_chroot(const int argc, char **arg) { // Now go on and run otapreopt. - const char* argv[1 + DEXOPT_PARAM_COUNT + 1]; - CHECK_EQ(static_cast<size_t>(argc), DEXOPT_PARAM_COUNT + 1); + // Incoming: cmd + status-fd + target-slot + "dexopt" + dexopt-params + null + // Outgoing: cmd + target-slot + "dexopt" + dexopt-params + null + constexpr size_t kInArguments = 1 // Binary name. + + 1 // status file descriptor. + + 1 // target-slot. + + 1 // "dexopt." + + DEXOPT_PARAM_COUNT // dexopt parameters. + + 1; // null termination. + constexpr size_t kOutArguments = 1 // Binary name. + + 1 // target-slot. + + 1 // "dexopt." + + DEXOPT_PARAM_COUNT // dexopt parameters. + + 1; // null termination. + const char* argv[kOutArguments]; + if (static_cast<size_t>(argc) != kInArguments - 1 /* null termination */) { + LOG(ERROR) << "Unexpected argument size " + << argc + << " vs " + << (kInArguments - 1); + for (size_t i = 0; i < static_cast<size_t>(argc); ++i) { + if (arg[i] == nullptr) { + LOG(ERROR) << "(null)"; + } else { + LOG(ERROR) << "\"" << arg[i] << "\""; + } + } + exit(206); + } argv[0] = "/system/bin/otapreopt"; - for (size_t i = 1; i <= DEXOPT_PARAM_COUNT; ++i) { - argv[i] = arg[i]; + + // The first parameter is the status file descriptor, skip. + + for (size_t i = 1; i <= kOutArguments - 2 /* cmd + null */; ++i) { + argv[i] = arg[i + 1]; } - argv[DEXOPT_PARAM_COUNT + 1] = nullptr; + argv[kOutArguments - 1] = nullptr; execv(argv[0], (char * const *)argv); PLOG(ERROR) << "execv(OTAPREOPT) failed."; diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh index 394c244336..8af4a901f5 100644 --- a/cmds/installd/otapreopt_script.sh +++ b/cmds/installd/otapreopt_script.sh @@ -18,11 +18,25 @@ # This script will run as a postinstall step to drive otapreopt. +TARGET_SLOT="$1" STATUS_FD="$2" # Maximum number of packages/steps. MAXIMUM_PACKAGES=1000 +# Compute target slot suffix. +# TODO: Once bootctl is not restricted, we should query from there. Or get this from +# update_engine as a parameter. +if [ "$TARGET_SLOT" = "0" ] ; then + TARGET_SLOT_SUFFIX="_a" +elif [ "$TARGET_SLOT" = "1" ] ; then + TARGET_SLOT_SUFFIX="_b" +else + echo "Unknown target slot $TARGET_SLOT" + exit 1 +fi + + PREPARE=$(cmd otadexopt prepare) # Note: Ignore preparation failures. Step and done will fail and exit this. # This is necessary to support suspends - the OTA service will keep @@ -33,7 +47,9 @@ print -u${STATUS_FD} "global_progress $PROGRESS" i=0 while ((i<MAXIMUM_PACKAGES)) ; do - cmd otadexopt step + DEXOPT_PARAMS=$(cmd otadexopt next) + + /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&- PROGRESS=$(cmd otadexopt progress) print -u${STATUS_FD} "global_progress $PROGRESS" diff --git a/cmds/installd/otapreopt_slot.sh b/cmds/installd/otapreopt_slot.sh new file mode 100644 index 0000000000..d51ab706e6 --- /dev/null +++ b/cmds/installd/otapreopt_slot.sh @@ -0,0 +1,36 @@ +#!/system/bin/sh + +# +# Copyright (C) 2016 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. +# + +# This script will move artifacts for the currently active slot. + +SLOT_SUFFIX=$(getprop ro.boot.slot_suffix) +if test -n "$SLOT_SUFFIX" ; then + if test -d /data/ota/$SLOT_SUFFIX/dalvik-cache ; then + log -p i -t otapreopt_slot "Moving A/B artifacts for slot ${SLOT_SUFFIX}." + rm -rf /data/dalvik-cache/* + mv /data/ota/$SLOT_SUFFIX/dalvik-cache/* /data/dalvik-cache/ + rmdir /data/ota/$SLOT_SUFFIX/dalvik-cache + rmdir /data/ota/$SLOT_SUFFIX + else + log -p i -t otapreopt_slot "No A/B artifacts found for slot ${SLOT_SUFFIX}." + fi + exit 0 +else + log -p w -t otapreopt_slot "Slot property empty." + exit 1 +fi diff --git a/cmds/installd/otapreopt_utils.h b/cmds/installd/otapreopt_utils.h new file mode 100644 index 0000000000..436e554944 --- /dev/null +++ b/cmds/installd/otapreopt_utils.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OTAPREOPT_UTILS_H_ +#define OTAPREOPT_UTILS_H_ + +#include <regex> + +namespace android { +namespace installd { + +static inline bool ValidateTargetSlotSuffix(const std::string& input) { + std::regex slot_suffix_regex("[a-zA-Z0-9_]+"); + std::smatch slot_suffix_match; + return std::regex_match(input, slot_suffix_match, slot_suffix_regex); +} + +} // namespace installd +} // namespace android + +#endif // OTAPREOPT_UTILS_H_ diff --git a/docs/Makefile b/docs/Makefile index 5104d81c20..c655e0c09b 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,13 +1,12 @@ HEADERS := $(wildcard ../include/android/*.h) -all: html jd +all: html website html: $(HEADERS) Doxyfile mkdir -p html doxygen -jd: $(HEADERS) Doxyfile header.jd - mkdir -p jd - HTML_HEADER=header.jd HTML_FOOTER=footer.jd HTML_OUTPUT=jd doxygen - for file in jd/*.html; do mv "$${file}" "$${file/.html/.jd}"; done - rm -f jd/index.jd +website: $(HEADERS) Doxyfile header.html + mkdir -p website + HTML_HEADER=header.html HTML_FOOTER=footer.html HTML_OUTPUT=website doxygen + rm -f website/index.html diff --git a/docs/footer.html b/docs/footer.html new file mode 100644 index 0000000000..308b1d01b6 --- /dev/null +++ b/docs/footer.html @@ -0,0 +1,2 @@ +</body> +</html> diff --git a/docs/footer.jd b/docs/footer.jd deleted file mode 100644 index e69de29bb2..0000000000 --- a/docs/footer.jd +++ /dev/null diff --git a/docs/header.html b/docs/header.html new file mode 100644 index 0000000000..04727b3dab --- /dev/null +++ b/docs/header.html @@ -0,0 +1,10 @@ +<html devsite> +<head> + <meta name="top_category" value="ndk" /> + <meta name="subcategory" value="reference" /> + <meta name="book_path" value="/ndk/reference/_book.yaml" /> + <title>$title</title> + <link rel="stylesheet" type="text/css" href="doxygen-dac.css"> +</head> +<body> +<div id="top"><!-- we must have this tag, it's closed by doxygen. ¯\_(ツ)_/¯ --> diff --git a/docs/header.jd b/docs/header.jd deleted file mode 100644 index e50f41b03b..0000000000 --- a/docs/header.jd +++ /dev/null @@ -1,3 +0,0 @@ -page.title=$title -page.customHeadTag=<link rel="stylesheet" type="text/css" href="doxygen-dac.css"> -@jd:body diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h index 579ffb9db5..838632c26d 100644 --- a/include/gui/BufferQueueProducer.h +++ b/include/gui/BufferQueueProducer.h @@ -170,9 +170,6 @@ public: // See IGraphicBufferProducer::getConsumerName virtual String8 getConsumerName() const override; - // See IGraphicBufferProducer::getNextFrameNumber - virtual uint64_t getNextFrameNumber() const override; - // See IGraphicBufferProducer::setSharedBufferMode virtual status_t setSharedBufferMode(bool sharedBufferMode) override; diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index 47bb43a009..c62bc5899c 100644 --- a/include/gui/IGraphicBufferProducer.h +++ b/include/gui/IGraphicBufferProducer.h @@ -361,24 +361,29 @@ public: inline void deflate(uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransformHint, - uint32_t* outNumPendingBuffers) const { + uint32_t* outNumPendingBuffers, + uint64_t* outNextFrameNumber) const { *outWidth = width; *outHeight = height; *outTransformHint = transformHint; *outNumPendingBuffers = numPendingBuffers; + *outNextFrameNumber = nextFrameNumber; } inline void inflate(uint32_t inWidth, uint32_t inHeight, - uint32_t inTransformHint, uint32_t inNumPendingBuffers) { + uint32_t inTransformHint, uint32_t inNumPendingBuffers, + uint64_t inNextFrameNumber) { width = inWidth; height = inHeight; transformHint = inTransformHint; numPendingBuffers = inNumPendingBuffers; + nextFrameNumber = inNextFrameNumber; } private: uint32_t width; uint32_t height; uint32_t transformHint; uint32_t numPendingBuffers; + uint64_t nextFrameNumber{0}; }; virtual status_t queueBuffer(int slot, const QueueBufferInput& input, @@ -523,9 +528,6 @@ public: // Returns the name of the connected consumer. virtual String8 getConsumerName() const = 0; - // Returns the number of the next frame which will be dequeued. - virtual uint64_t getNextFrameNumber() const = 0; - // Used to enable/disable shared buffer mode. // // When shared buffer mode is enabled the first buffer that is queued or diff --git a/include/gui/Surface.h b/include/gui/Surface.h index b9ffc49690..8177ec66af 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -375,6 +375,8 @@ private: nsecs_t mLastQueueDuration = 0; Condition mQueueBufferCondition; + + uint64_t mNextFrameNumber; }; namespace view { diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h index 03801cafe1..bae2c185d4 100644 --- a/include/media/openmax/OMX_AsString.h +++ b/include/media/openmax/OMX_AsString.h @@ -973,8 +973,8 @@ inline static const char *asString(OMX_VIDEO_VP9LEVELTYPE i, const char *def = " inline static const char *asString( OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE i, const char *def = "??") { switch (i) { - case OMX_VIDEO_VPXTemporalLayerPatternNone: return "VPXTemporalLayerPatternNone"; - case OMX_VIDEO_VPXTemporalLayerPatternWebRTC: return "VPXTemporalLayerPatternWebRTC"; + case OMX_VIDEO_VPXTemporalLayerPatternNone: return "None"; + case OMX_VIDEO_VPXTemporalLayerPatternWebRTC: return "WebRTC"; default: return def; } } @@ -1022,6 +1022,16 @@ inline static const char *asString(OMX_VIDEO_HEVCLEVELTYPE i, const char *def = } } +inline static const char *asString( + OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE i, const char *def = "??") { + switch (i) { + case OMX_VIDEO_AndroidTemporalLayeringPatternNone: return "None"; + case OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC: return "WebRTC"; + case OMX_VIDEO_AndroidTemporalLayeringPatternAndroid: return "Android"; + default: return def; + } +} + #endif // AS_STRING_FOR_OMX_VIDEOEXT_H #endif // OMX_VideoExt_h diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h index 1724576ad5..b688d1d949 100644 --- a/include/media/openmax/OMX_IndexExt.h +++ b/include/media/openmax/OMX_IndexExt.h @@ -78,6 +78,8 @@ typedef enum OMX_INDEXEXTTYPE { OMX_IndexParamVideoHevc, /**< reference: OMX_VIDEO_PARAM_HEVCTYPE */ OMX_IndexParamSliceSegments, /**< reference: OMX_VIDEO_SLICESEGMENTSTYPE */ OMX_IndexConfigAndroidIntraRefresh, /**< reference: OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE */ + OMX_IndexParamAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE */ + OMX_IndexConfigAndroidVideoTemporalLayering, /**< reference: OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE */ /* Image & Video common configurations */ OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000, diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h index bf15ee4a5e..2c02431730 100644 --- a/include/media/openmax/OMX_VideoExt.h +++ b/include/media/openmax/OMX_VideoExt.h @@ -170,7 +170,11 @@ typedef struct OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; OMX_U32 nPortIndex; - OMX_U32 nKeyFrameInterval; + OMX_U32 nKeyFrameInterval; // distance between consecutive key_frames (including one + // of the key_frames). 0 means interval is unspecified and + // can be freely chosen by the codec. 1 means a stream of + // only key_frames. + OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE eTemporalPattern; OMX_U32 nTemporalLayerCount; OMX_U32 nTemporalLayerBitrateRatio[OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS]; @@ -227,7 +231,10 @@ typedef struct OMX_VIDEO_PARAM_HEVCTYPE { OMX_U32 nPortIndex; OMX_VIDEO_HEVCPROFILETYPE eProfile; OMX_VIDEO_HEVCLEVELTYPE eLevel; - OMX_U32 nKeyFrameInterval; + OMX_U32 nKeyFrameInterval; // distance between consecutive I-frames (including one + // of the I frames). 0 means interval is unspecified and + // can be freely chosen by the codec. 1 means a stream of + // only I frames. } OMX_VIDEO_PARAM_HEVCTYPE; /** Structure to define if dependent slice segments should be used */ @@ -289,7 +296,7 @@ typedef enum OMX_VIDEO_DOLBYVISIONLEVELTYPE { * nVersion : OMX specification version information * nPortIndex : Port that this structure applies to * nRefreshPeriod : Intra refreh period in frames. Value 0 means disable intra refresh -*/ + */ typedef struct OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE { OMX_U32 nSize; OMX_VERSIONTYPE nVersion; @@ -297,6 +304,95 @@ typedef struct OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE { OMX_U32 nRefreshPeriod; } OMX_VIDEO_CONFIG_ANDROID_INTRAREFRESHTYPE; +/** Maximum number of temporal layers supported by AVC/HEVC */ +#define OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS 8 + +/** temporal layer patterns */ +typedef enum OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE { + OMX_VIDEO_AndroidTemporalLayeringPatternNone = 0, + // pattern as defined by WebRTC + OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC = 1 << 0, + // pattern where frames in any layer other than the base layer only depend on at most the very + // last frame from each preceding layer (other than the base layer.) + OMX_VIDEO_AndroidTemporalLayeringPatternAndroid = 1 << 1, +} OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE; + +/** + * Android specific param for configuration of temporal layering. + * Android only supports temporal layering where successive layers each double the + * previous layer's framerate. + * NOTE: Reading this parameter at run-time SHALL return actual run-time values. + * + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to (output port for encoders) + * eSupportedPatterns : A bitmask of supported layering patterns + * nLayerCountMax : Max number of temporal coding layers supported + * by the encoder (must be at least 1, 1 meaning temporal layering + * is NOT supported) + * nBLayerCountMax : Max number of layers that can contain B frames + * (0) to (nLayerCountMax - 1) + * ePattern : Layering pattern. + * nPLayerCountActual : Number of temporal layers to be coded with non-B frames, + * starting from and including the base-layer. + * (1 to nLayerCountMax - nBLayerCountActual) + * If nPLayerCountActual is 1 and nBLayerCountActual is 0, temporal + * layering is disabled. Otherwise, it is enabled. + * nBLayerCountActual : Number of temporal layers to be coded with B frames, + * starting after non-B layers. + * (0 to nBLayerCountMax) + * bBitrateRatiosSpecified : Flag to indicate if layer-wise bitrate + * distribution is specified. + * nBitrateRatios : Bitrate ratio (100 based) per layer (index 0 is base layer). + * Honored if bBitrateRatiosSpecified is set. + * i.e for 4 layers with desired distribution (25% 25% 25% 25%), + * nBitrateRatio = {25, 50, 75, 100, ... } + * Values in indices not less than 'the actual number of layers + * minus 1' MAY be ignored and assumed to be 100. + */ +typedef struct OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE eSupportedPatterns; + OMX_U32 nLayerCountMax; + OMX_U32 nBLayerCountMax; + OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE ePattern; + OMX_U32 nPLayerCountActual; + OMX_U32 nBLayerCountActual; + OMX_BOOL bBitrateRatiosSpecified; + OMX_U32 nBitrateRatios[OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS]; +} OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE; + +/** + * Android specific config for changing the temporal-layer count or + * bitrate-distribution at run-time. + * + * nSize : Size of the structure in bytes + * nVersion : OMX specification version information + * nPortIndex : Port that this structure applies to (output port for encoders) + * ePattern : Layering pattern. + * nPLayerCountActual : Number of temporal layers to be coded with non-B frames. + * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) + * nBLayerCountActual : Number of temporal layers to be coded with B frames. + * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) + * bBitrateRatiosSpecified : Flag to indicate if layer-wise bitrate + * distribution is specified. + * nBitrateRatios : Bitrate ratio (100 based, Q16 values) per layer (0 is base layer). + * Honored if bBitrateRatiosSpecified is set. + * (same OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE limits apply.) + */ +typedef struct OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE { + OMX_U32 nSize; + OMX_VERSIONTYPE nVersion; + OMX_U32 nPortIndex; + OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE ePattern; + OMX_U32 nPLayerCountActual; + OMX_U32 nBLayerCountActual; + OMX_BOOL bBitrateRatiosSpecified; + OMX_U32 nBitrateRatios[OMX_VIDEO_ANDROID_MAXTEMPORALLAYERS]; +} OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE; + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index c7e8ff2e71..e88ae29518 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -1795,15 +1795,16 @@ status_t Parcel::readUtf8FromUtf16(std::string* str) const { return NO_ERROR; } - ssize_t utf8Size = utf16_to_utf8_length(src, utf16Size); - if (utf8Size < 0) { + // Allow for closing '\0' + ssize_t utf8Size = utf16_to_utf8_length(src, utf16Size) + 1; + if (utf8Size < 1) { return BAD_VALUE; } // Note that while it is probably safe to assume string::resize keeps a - // spare byte around for the trailing null, we're going to be explicit. - str->resize(utf8Size + 1); - utf16_to_utf8(src, utf16Size, &((*str)[0])); + // spare byte around for the trailing null, we still pass the size including the trailing null str->resize(utf8Size); + utf16_to_utf8(src, utf16Size, &((*str)[0]), utf8Size); + str->resize(utf8Size - 1); return NO_ERROR; } diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 49db4aa815..b7b56f03f2 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -900,7 +900,8 @@ status_t BufferQueueProducer::queueBuffer(int slot, output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, mCore->mTransformHint, - static_cast<uint32_t>(mCore->mQueue.size())); + static_cast<uint32_t>(mCore->mQueue.size()), + mCore->mFrameCounter + 1); ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size()); mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); @@ -1107,7 +1108,8 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, mCore->mConnectedApi = api; output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, mCore->mTransformHint, - static_cast<uint32_t>(mCore->mQueue.size())); + static_cast<uint32_t>(mCore->mQueue.size()), + mCore->mFrameCounter + 1); // Set up a death notification so that we can disconnect // automatically if the remote producer dies @@ -1342,14 +1344,6 @@ String8 BufferQueueProducer::getConsumerName() const { return mConsumerName; } -uint64_t BufferQueueProducer::getNextFrameNumber() const { - ATRACE_CALL(); - - Mutex::Autolock lock(mCore->mMutex); - uint64_t nextFrameNumber = mCore->mFrameCounter + 1; - return nextFrameNumber; -} - status_t BufferQueueProducer::setSharedBufferMode(bool sharedBufferMode) { ATRACE_CALL(); BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode); diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index c177922492..fbd704d03d 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -50,7 +50,6 @@ enum { GET_CONSUMER_NAME, SET_MAX_DEQUEUED_BUFFER_COUNT, SET_ASYNC_MODE, - GET_NEXT_FRAME_NUMBER, SET_SHARED_BUFFER_MODE, SET_AUTO_REFRESH, SET_DEQUEUE_TIMEOUT, @@ -134,7 +133,11 @@ public: bool nonNull = reply.readInt32(); if (nonNull) { *fence = new Fence(); - reply.read(**fence); + result = reply.read(**fence); + if (result != NO_ERROR) { + fence->clear(); + return result; + } } result = reply.readInt32(); return result; @@ -172,12 +175,21 @@ public: bool nonNull = reply.readInt32(); if (nonNull) { *outBuffer = new GraphicBuffer; - reply.read(**outBuffer); + result = reply.read(**outBuffer); + if (result != NO_ERROR) { + outBuffer->clear(); + return result; + } } nonNull = reply.readInt32(); if (nonNull) { *outFence = new Fence; - reply.read(**outFence); + result = reply.read(**outFence); + if (result != NO_ERROR) { + outBuffer->clear(); + outFence->clear(); + return result; + } } } return result; @@ -334,18 +346,6 @@ public: return reply.readString8(); } - virtual uint64_t getNextFrameNumber() const { - Parcel data, reply; - data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); - status_t result = remote()->transact(GET_NEXT_FRAME_NUMBER, data, &reply); - if (result != NO_ERROR) { - ALOGE("getNextFrameNumber failed to transact: %d", result); - return 0; - } - uint64_t frameNumber = reply.readUint64(); - return frameNumber; - } - virtual status_t setSharedBufferMode(bool sharedBufferMode) { Parcel data, reply; data.writeInterfaceToken( @@ -561,9 +561,11 @@ status_t BnGraphicBufferProducer::onTransact( case ATTACH_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp<GraphicBuffer> buffer = new GraphicBuffer(); - data.read(*buffer.get()); + status_t result = data.read(*buffer.get()); int slot = 0; - int result = attachBuffer(&slot, buffer); + if (result == NO_ERROR) { + result = attachBuffer(&slot, buffer); + } reply->writeInt32(slot); reply->writeInt32(result); return NO_ERROR; @@ -584,8 +586,10 @@ status_t BnGraphicBufferProducer::onTransact( CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); sp<Fence> fence = new Fence(); - data.read(*fence.get()); - status_t result = cancelBuffer(buf, fence); + status_t result = data.read(*fence.get()); + if (result == NO_ERROR) { + result = cancelBuffer(buf, fence); + } reply->writeInt32(result); return NO_ERROR; } @@ -659,12 +663,6 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeString8(getConsumerName()); return NO_ERROR; } - case GET_NEXT_FRAME_NUMBER: { - CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - uint64_t frameNumber = getNextFrameNumber(); - reply->writeUint64(frameNumber); - return NO_ERROR; - } case SET_SHARED_BUFFER_MODE: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); bool sharedBufferMode = data.readInt32(); diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index af559133a9..dbf811462d 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -48,7 +48,8 @@ Surface::Surface( mSharedBufferMode(false), mAutoRefresh(false), mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT), - mSharedBufferHasBeenQueued(false) + mSharedBufferHasBeenQueued(false), + mNextFrameNumber(1) { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; @@ -116,7 +117,8 @@ status_t Surface::setGenerationNumber(uint32_t generation) { } uint64_t Surface::getNextFrameNumber() const { - return mGraphicBufferProducer->getNextFrameNumber(); + Mutex::Autolock lock(mMutex); + return mNextFrameNumber; } String8 Surface::getConsumerName() const { @@ -508,7 +510,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { uint32_t numPendingBuffers = 0; uint32_t hint = 0; output.deflate(&mDefaultWidth, &mDefaultHeight, &hint, - &numPendingBuffers); + &numPendingBuffers, &mNextFrameNumber); // Disable transform hint if sticky transform is set. if (mStickyTransform == 0) { @@ -820,7 +822,7 @@ int Surface::connect(int api, const sp<IProducerListener>& listener) { uint32_t numPendingBuffers = 0; uint32_t hint = 0; output.deflate(&mDefaultWidth, &mDefaultHeight, &hint, - &numPendingBuffers); + &numPendingBuffers, &mNextFrameNumber); // Disable transform hint if sticky transform is set. if (mStickyTransform == 0) { @@ -1340,8 +1342,7 @@ status_t Surface::unlockAndPost() bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) { Mutex::Autolock lock(mMutex); - uint64_t currentFrame = mGraphicBufferProducer->getNextFrameNumber(); - if (currentFrame > lastFrame) { + if (mNextFrameNumber > lastFrame) { return true; } return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK; diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index 45b64639d2..9f3304731e 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -370,13 +370,16 @@ TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) { uint32_t height; uint32_t transformHint; uint32_t numPendingBuffers; + uint64_t nextFrameNumber; - output.deflate(&width, &height, &transformHint, &numPendingBuffers); + output.deflate(&width, &height, &transformHint, &numPendingBuffers, + &nextFrameNumber); EXPECT_EQ(DEFAULT_WIDTH, width); EXPECT_EQ(DEFAULT_HEIGHT, height); EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint); EXPECT_EQ(1u, numPendingBuffers); // since queueBuffer was called exactly once + EXPECT_EQ(2u, nextFrameNumber); } // Buffer was not in the dequeued state diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index bef5f02aa3..2e186985e6 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -621,6 +621,24 @@ typedef EGLAPI EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDRO #define EGL_MUTABLE_RENDER_BUFFER_BIT_KHR 0x1000 #endif +#ifndef EGL_ANDROID_get_frame_timestamps +#define EGL_ANDROID_get_frame_timestamps 1 +#define EGL_TIMESTAMPS_ANDROID 0x314D +#define EGL_QUEUE_TIME_ANDROID 0x314E +#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F +#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430 +#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431 +#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432 +#define EGL_READS_DONE_TIME_ANDROID 0x3433 +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); +EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp); +#else +typedef EGLAPI EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); +typedef EGLAPI EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp); +#endif +#endif + #ifdef __cplusplus } #endif diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index e793852caa..7a0cce48b2 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -84,6 +84,7 @@ extern char const * const gBuiltinExtensionString = "EGL_KHR_swap_buffers_with_damage " "EGL_ANDROID_create_native_client_buffer " "EGL_ANDROID_front_buffer_auto_refresh " + "EGL_ANDROID_get_frame_timestamps " ; extern char const * const gExtensionString = "EGL_KHR_image " // mandatory @@ -207,6 +208,12 @@ static const extention_map_t sExtensionMap[] = { (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, { "eglCreateStreamFromFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, + + // EGL_ANDROID_get_frame_timestamps + { "eglGetFrameTimestampsANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, + { "eglQueryTimestampSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID }, }; /* @@ -1196,7 +1203,7 @@ EGLBoolean eglSurfaceAttrib( if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE); - egl_surface_t const * const s = get_surface(surface); + egl_surface_t * const s = get_surface(surface); if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) { int err = native_window_set_auto_refresh(s->win.get(), @@ -1205,6 +1212,11 @@ EGLBoolean eglSurfaceAttrib( setError(EGL_BAD_SURFACE, EGL_FALSE); } + if (attribute == EGL_TIMESTAMPS_ANDROID) { + s->enableTimestamps = value; + return EGL_TRUE; + } + if (s->cnx->egl.eglSurfaceAttrib) { return s->cnx->egl.eglSurfaceAttrib( dp->disp.dpy, s->surface, attribute, value); @@ -1935,3 +1947,103 @@ EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, return EGL_FALSE; } + +EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, + EGLnsecsANDROID *values) +{ + clearError(); + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + setError(EGL_BAD_DISPLAY, EGL_FALSE); + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->enableTimestamps) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + nsecs_t* postedTime = nullptr; + nsecs_t* acquireTime = nullptr; + nsecs_t* refreshStartTime = nullptr; + nsecs_t* GLCompositionDoneTime = nullptr; + nsecs_t* displayRetireTime = nullptr; + nsecs_t* releaseTime = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (timestamps[i]) { + case EGL_QUEUE_TIME_ANDROID: + postedTime = &values[i]; + break; + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + acquireTime = &values[i]; + break; + case EGL_COMPOSITION_START_TIME_ANDROID: + refreshStartTime = &values[i]; + break; + case EGL_COMPOSITION_FINISHED_TIME_ANDROID: + GLCompositionDoneTime = &values[i]; + break; + case EGL_DISPLAY_RETIRE_TIME_ANDROID: + displayRetireTime = &values[i]; + break; + case EGL_READS_DONE_TIME_ANDROID: + releaseTime = &values[i]; + break; + default: + setError(EGL_BAD_PARAMETER, EGL_FALSE); + return EGL_FALSE; + } + } + + status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo, + postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime, + displayRetireTime, releaseTime); + + if (ret != NO_ERROR) { + setError(EGL_BAD_ACCESS, EGL_FALSE); + return EGL_FALSE; + } + + return EGL_TRUE; +} + +EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint timestamp) +{ + clearError(); + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + setError(EGL_BAD_DISPLAY, EGL_FALSE); + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + switch (timestamp) { + case EGL_QUEUE_TIME_ANDROID: + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + case EGL_COMPOSITION_START_TIME_ANDROID: + case EGL_COMPOSITION_FINISHED_TIME_ANDROID: + case EGL_DISPLAY_RETIRE_TIME_ANDROID: + case EGL_READS_DONE_TIME_ANDROID: + return EGL_TRUE; + default: + return EGL_FALSE; + } +} diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp index 90f27d1c7d..cfecf77021 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -68,7 +68,7 @@ egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win, EGLSurface surface, egl_connection_t const* cnx) : egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx), - connected(true) + enableTimestamps(false), connected(true) { if (win) { getDisplay()->onWindowSurfaceCreated(); diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h index 8f3b9cb2b5..97eda4ce79 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -139,6 +139,7 @@ public: EGLConfig config; sp<ANativeWindow> win; egl_connection_t const* cnx; + bool enableTimestamps; private: bool connected; void disconnect(); diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt new file mode 100644 index 0000000000..30337ad7a9 --- /dev/null +++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt @@ -0,0 +1,145 @@ +Name + + ANDROID_get_frame_timestamps + +Name Strings + + EGL_ANDROID_get_frame_timestamps + +Contributors + + Pablo Ceballos + +Contact + + Pablo Ceballos, Google Inc. (pceballos 'at' google.com) + +Status + + Draft + +Version + + Version 1, May 31, 2016 + +Number + + EGL Extension #XXX + +Dependencies + + Requires EGL 1.2 + + This extension is written against the wording of the EGL 1.5 Specification + +Overview + + This extension allows querying various timestamps related to the composition + and display of window surfaces. + + Some examples of how this might be used: + - The display retire time can be used to calculate end-to-end latency of + the entire graphics pipeline. + - The queue time and rendering complete time can be used to determine + how long the application's rendering took to complete. Likewise, the + composition start time and finish time can be used to determine how + long the compositor's rendering work took. In combination these can be + used to help determine if the system is GPU or CPU bound. + +New Types + + /* + * EGLnsecsANDROID is a signed integer type for representing a time in + * nanoseconds. + */ + #include <khrplatform.h> + typedef khronos_stime_nanoseconds_t EGLnsecsANDROID; + +New Procedures and Functions + + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, + EGLnsecsANDROID *values); + + EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface + surface, EGLint timestamp); + +New Tokens + + EGL_TIMESTAMPS_ANDROID 0x314D + EGL_QUEUE_TIME_ANDROID 0x314E + EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F + EGL_COMPOSITION_START_TIME_ANDROID 0x3430 + EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431 + EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432 + EGL_READS_DONE_TIME_ANDROID 0x3433 + +Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6 +"Surface Attributes", page 43: + + If attribute is EGL_TIMESTAMPS_ANDROID, then values specifies whether to + enable/disable timestamp collection for this surface. A value of EGL_TRUE + enables timestamp collection, while a value of EGL_FALSE disables it. The + initial value is false. If surface is not a window surface this has no + effect. + +Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors) + + Add a new subsection under Section 3, + + "3.13 Composition and Display Timestamps + + The function + + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface + surface, EGLint framesAgo, EGLint numTimestamps, + const EGLint *timestamps, EGLnsecsANDROID *values); + + allows querying various timestamps related to the composition and display of + a window surface. + + The framesAgo parameter indicates how many frames before the last posted + frame to query. So a value of zero would indicate that the query is for the + last posted frame. Note that the implementation maintains a limited history + of timestamp data. If a query is made for a frame whose timestamp history + no longer exists then EGL_BAD_ACCESS is generated. If timestamp collection + has not been enabled for the surface then EGL_BAD_SURFACE is generated. + Timestamps for events that will not occur or have not yet occurred will be + zero. Timestamp queries that are not supported will generate an + EGL_BAD_PARAMETER error. If any error is generated the function will return + EGL_FALSE. + + The eglGetFrameTimestampsANDROID function takes an array of timestamps to + query and returns timestamps in the corresponding indices of the values + array. The possible timestamps that can be queried are: + - EGL_QUEUE_TIME_ANDROID - The time this frame was queued by the + application. + - EGL_RENDERING_COMPLETE_TIME_ANDROID - The time when all of the + application's rendering to the surface was completed. + - EGL_COMPOSITION_START_TIME_ANDROID - The time at which the compositor + began preparing composition for this frame. + - EGL_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the + compositor's rendering work for this frame finished. This will be zero + if composition was handled by the display and the compositor didn't do + any rendering. + - EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was + replaced by the next frame on-screen. + - EGL_READS_DONE_TIME_ANDROID - The time at which all reads for the + purpose of display/composition were completed for this frame. + + Not all implementations may support all off the above timestamp queries. The + function + + EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface + surface, EGLint timestamp); + + allows querying which timestamps are supported on the implementation." + +Issues + + None + +Revision History + +#1 (Pablo Ceballos, May 31, 2016) + - Initial draft. diff --git a/opengl/specs/README b/opengl/specs/README index 8f1eaf3cf6..f0c024ee66 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -19,4 +19,11 @@ for use by Android extensions. 0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop) 0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop) 0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh) -0x314D - 0x314F (unused) +0x314D EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x314E EGL_QUEUE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x314F EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3430 EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3431 EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3432 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3433 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3434 - 0x343F (unused) diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index fb83effd25..0c4dc26917 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -435,15 +435,15 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { continue; } if (reg_info.mActivated) { - result.appendFormat("%02d:%02d:%02d activated package=%s handle=0x%08x " - "samplingRate=%dus maxReportLatency=%dus\n", - reg_info.mHour, reg_info.mMin, reg_info.mSec, - reg_info.mPackageName.string(), reg_info.mSensorHandle, - reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs); + result.appendFormat("%02d:%02d:%02d activated handle=0x%08x " + "samplingRate=%dus maxReportLatency=%dus package=%s\n", + reg_info.mHour, reg_info.mMin, reg_info.mSec, reg_info.mSensorHandle, + reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs, + reg_info.mPackageName.string()); } else { - result.appendFormat("%02d:%02d:%02d de-activated package=%s handle=0x%08x\n", + result.appendFormat("%02d:%02d:%02d de-activated handle=0x%08x package=%s\n", reg_info.mHour, reg_info.mMin, reg_info.mSec, - reg_info.mPackageName.string(), reg_info.mSensorHandle); + reg_info.mSensorHandle, reg_info.mPackageName.string()); } currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE; diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index c8de62190f..68d4154a49 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -53,7 +53,7 @@ // For older HALs which don't support batching, use a smaller socket buffer size. #define SOCKET_BUFFER_SIZE_NON_BATCHED 4 * 1024 -#define SENSOR_REGISTRATIONS_BUF_SIZE 20 +#define SENSOR_REGISTRATIONS_BUF_SIZE 200 namespace android { // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 26297943b5..133e5f1f6d 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -359,6 +359,27 @@ std::shared_ptr<const HWC2::Display::Config> return config; } +std::vector<int32_t> HWComposer::getColorModes(int32_t displayId) const { + std::vector<int32_t> modes; + + if (!isValidDisplay(displayId)) { + ALOGE("getColorModes: Attempted to access invalid display %d", + displayId); + return modes; + } + const std::shared_ptr<HWC2::Display>& hwcDisplay = + mDisplayData[displayId].hwcDisplay; + + auto error = hwcDisplay->getColorModes(&modes); + if (error != HWC2::Error::None) { + ALOGE("getColorModes failed for display %d: %s (%d)", displayId, + to_string(error).c_str(), static_cast<int32_t>(error)); + return std::vector<int32_t>(); + } + + return modes; +} + void HWComposer::setVsyncEnabled(int32_t disp, HWC2::Vsync enabled) { if (disp < 0 || disp >= HWC_DISPLAY_VIRTUAL) { ALOGD("setVsyncEnabled: Ignoring for virtual display %d", disp); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index b88e2501fe..aa233df1db 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -137,15 +137,6 @@ public: void setVsyncEnabled(int32_t disp, HWC2::Vsync enabled); - struct DisplayConfig { - uint32_t width; - uint32_t height; - float xdpi; - float ydpi; - nsecs_t refresh; - int colorTransform; - }; - // Query display parameters. Pass in a display index (e.g. // HWC_DISPLAY_PRIMARY). nsecs_t getRefreshTimestamp(int32_t disp) const; @@ -158,6 +149,8 @@ public: std::shared_ptr<const HWC2::Display::Config> getActiveConfig(int32_t displayId) const; + std::vector<int32_t> getColorModes(int32_t displayId) const; + // for debugging ---------------------------------------------------------- void dump(String8& out) const; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index c0baa49f85..61bb0bd8d9 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -304,8 +304,11 @@ void VirtualDisplaySurface::dumpAsString(String8& /* result */) const { void VirtualDisplaySurface::resizeBuffers(const uint32_t w, const uint32_t h) { uint32_t tmpW, tmpH, transformHint, numPendingBuffers; - mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers); - mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers); + uint64_t nextFrameNumber; + mQueueBufferOutput.deflate(&tmpW, &tmpH, &transformHint, &numPendingBuffers, + &nextFrameNumber); + mQueueBufferOutput.inflate(w, h, transformHint, numPendingBuffers, + nextFrameNumber); mSinkBufferWidth = w; mSinkBufferHeight = h; @@ -586,10 +589,6 @@ String8 VirtualDisplaySurface::getConsumerName() const { return String8("VirtualDisplaySurface"); } -uint64_t VirtualDisplaySurface::getNextFrameNumber() const { - return 0; -} - status_t VirtualDisplaySurface::setSharedBufferMode(bool /*sharedBufferMode*/) { ALOGE("setSharedBufferMode not supported on VirtualDisplaySurface"); return INVALID_OPERATION; @@ -620,8 +619,9 @@ status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const { void VirtualDisplaySurface::updateQueueBufferOutput( const QueueBufferOutput& qbo) { uint32_t w, h, transformHint, numPendingBuffers; - qbo.deflate(&w, &h, &transformHint, &numPendingBuffers); - mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers); + uint64_t nextFrameNumber; + qbo.deflate(&w, &h, &transformHint, &numPendingBuffers, &nextFrameNumber); + mQueueBufferOutput.inflate(w, h, 0, numPendingBuffers, nextFrameNumber); } void VirtualDisplaySurface::resetPerFrameState() { diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index 5b82355dcd..bf9b39c50d 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -122,7 +122,6 @@ private: virtual status_t allowAllocation(bool allow); virtual status_t setGenerationNumber(uint32_t generationNumber); virtual String8 getConsumerName() const override; - virtual uint64_t getNextFrameNumber() const override; virtual status_t setSharedBufferMode(bool sharedBufferMode) override; virtual status_t setAutoRefresh(bool autoRefresh) override; virtual status_t setDequeueTimeout(nsecs_t timeout) override; diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index fd33d5cd3e..36cfa3718a 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -127,10 +127,6 @@ String8 MonitoredProducer::getConsumerName() const { return mProducer->getConsumerName(); } -uint64_t MonitoredProducer::getNextFrameNumber() const { - return mProducer->getNextFrameNumber(); -} - status_t MonitoredProducer::setSharedBufferMode(bool sharedBufferMode) { return mProducer->setSharedBufferMode(sharedBufferMode); } diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h index 71b6b5915d..f64fe51ef5 100644 --- a/services/surfaceflinger/MonitoredProducer.h +++ b/services/surfaceflinger/MonitoredProducer.h @@ -57,7 +57,6 @@ public: virtual status_t allowAllocation(bool allow); virtual status_t setGenerationNumber(uint32_t generationNumber); virtual String8 getConsumerName() const override; - virtual uint64_t getNextFrameNumber() const override; virtual status_t setDequeueTimeout(nsecs_t timeout) override; virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) override; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index c541668a82..5c7db2b32f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -462,6 +462,18 @@ void SurfaceFlinger::init() { mSFEventThread = new EventThread(sfVsyncSrc, *this); mEventQueue.setEventThread(mSFEventThread); + // set EventThread and SFEventThread to SCHED_FIFO for minimum jitter + struct sched_param param = {0}; + param.sched_priority = 1; + if (sched_setscheduler(mEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for EventThread"); + } + + if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for SFEventThread"); + } + + // Get a RenderEngine for the given display / config (can't fail) mRenderEngine = RenderEngine::create(mEGLDisplay, HAL_PIXEL_FORMAT_RGBA_8888); @@ -602,9 +614,6 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.fps = 1e9 / hwConfig->getVsyncPeriod(); info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS; - // TODO: Hook this back up - info.colorTransform = 0; - // This is how far in advance a buffer must be queued for // presentation at a given time. If you want a buffer to appear // on the screen at time N, you must submit the buffer before @@ -623,7 +632,18 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, // All non-virtual displays are currently considered secure. info.secure = true; - configs->push_back(info); + // DisplayManager expects each color mode to be its own display + // info record. + std::vector<int32_t> modes = getHwComposer().getColorModes(type); + + if (modes.size() == 0) { + info.colorTransform = 0; + configs->push_back(info); + } + for (int32_t mode : modes) { + info.colorTransform = mode; + configs->push_back(info); + } } return NO_ERROR; @@ -901,6 +921,15 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { ATRACE_CALL(); switch (what) { case MessageQueue::INVALIDATE: { + bool frameMissed = !mHadClientComposition && + mPreviousPresentFence != Fence::NO_FENCE && + mPreviousPresentFence->getSignalTime() == INT64_MAX; + ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); + if (frameMissed) { + signalLayerUpdate(); + break; + } + bool refreshNeeded = handleMessageTransaction(); refreshNeeded |= handleMessageInvalidate(); refreshNeeded |= mRepaintEverything; @@ -930,14 +959,6 @@ bool SurfaceFlinger::handleMessageTransaction() { bool SurfaceFlinger::handleMessageInvalidate() { ATRACE_CALL(); - bool frameMissed = !mHadClientComposition && - mPreviousPresentFence != Fence::NO_FENCE && - mPreviousPresentFence->getSignalTime() == INT64_MAX; - ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); - if (frameMissed) { - signalLayerUpdate(); - return false; - } return handlePageFlip(); } diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp index d84d4e1b7f..69fb8c523d 100644 --- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -459,6 +459,18 @@ void SurfaceFlinger::init() { mSFEventThread = new EventThread(sfVsyncSrc, *this); mEventQueue.setEventThread(mSFEventThread); + // set EventThread and SFEventThread to SCHED_FIFO for minimum jitter + struct sched_param param = {0}; + param.sched_priority = 1; + if (sched_setscheduler(mEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for EventThread"); + } + + if (sched_setscheduler(mSFEventThread->getTid(), SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO for SFEventThread"); + } + + // Initialize the H/W composer object. There may or may not be an // actual hardware composer underneath. mHwc = new HWComposer(this, diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index 97a1e8b62b..543d0c7711 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -16,6 +16,8 @@ #include <sys/resource.h> +#include <sched.h> + #include <cutils/sched_policy.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> @@ -61,6 +63,12 @@ int main(int, char**) { sp<GpuService> gpuservice = new GpuService(); sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false); + struct sched_param param = {0}; + param.sched_priority = 1; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO"); + } + // run surface flinger in this thread flinger->run(); |