Merge "Add bugreportwear service for Wear." into nyc-mr1-dev
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index d35099a..86df596 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -96,6 +96,17 @@
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_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 8999441..f4044b2 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 @@
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 @@
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 @@
+ (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 @@
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 @@
}
}
-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 @@
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 @@
}
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 c0c39c5..e990f1b 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 @@
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 6a67e29..93e1ce5 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 @@
// 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 @@
}
// 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 3e52346..c90beec 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 9d2f71b..facbc72 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -557,7 +557,7 @@
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 823b8ee..41732cc 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 5ff46e6..5093178 100644
--- a/cmds/installd/installd_deps.h
+++ b/cmds/installd/installd_deps.h
@@ -57,9 +57,6 @@
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 e1cfc9d..5fa972a 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 @@
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 @@
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 @@
//
// 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 @@
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 @@
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 @@
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;
+ }
+
+ 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 ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) {
- size_t index = 0;
+ 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 @@
}
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
////////////////////////////////////
// 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 @@
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 @@
}
}
+ 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 @@
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_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 @@
}
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 @@
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 @@
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 @@
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 @@
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 0000000..059ae75
--- /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 be0ff2e..5ea89e6 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 @@
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 @@
}
}
+ // 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 @@
// 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);
- argv[0] = "/system/bin/otapreopt";
- for (size_t i = 1; i <= DEXOPT_PARAM_COUNT; ++i) {
- argv[i] = arg[i];
+ // 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[DEXOPT_PARAM_COUNT + 1] = nullptr;
+ argv[0] = "/system/bin/otapreopt";
+
+ // 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[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 394c244..8af4a90 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 @@
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 0000000..d51ab70
--- /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 0000000..436e554
--- /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 5104d81..c655e0c 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 0000000..308b1d0
--- /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 e69de29..0000000
--- a/docs/footer.jd
+++ /dev/null
diff --git a/docs/header.html b/docs/header.html
new file mode 100644
index 0000000..04727b3
--- /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 e50f41b..0000000
--- 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 579ffb9..838632c 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -170,9 +170,6 @@
// 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 47bb43a..c62bc58 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -361,24 +361,29 @@
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 @@
// 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 b9ffc49..8177ec6 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -375,6 +375,8 @@
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 03801ca..bae2c18 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_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_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 1724576..b688d1d 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -78,6 +78,8 @@
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 bf15ee4..2c02431 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -170,7 +170,11 @@
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 @@
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 @@
* 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 @@
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 c7e8ff2..e88ae29 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1795,15 +1795,16 @@
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 49db4aa..b7b56f0 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -900,7 +900,8 @@
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 @@
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 @@
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 c177922..fbd704d 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -50,7 +50,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 af55913..dbf8114 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -48,7 +48,8 @@
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 @@
}
uint64_t Surface::getNextFrameNumber() const {
- return mGraphicBufferProducer->getNextFrameNumber();
+ Mutex::Autolock lock(mMutex);
+ return mNextFrameNumber;
}
String8 Surface::getConsumerName() const {
@@ -508,7 +510,7 @@
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 @@
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 @@
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 45b6463..9f33047 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -370,13 +370,16 @@
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 bef5f02..2e18698 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -621,6 +621,24 @@
#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 e793852..7a0cce4 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -84,6 +84,7 @@
"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 @@
(__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
{ "eglCreateStreamFromFileDescriptorKHR",
(__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+ // EGL_ANDROID_get_frame_timestamps
+ { "eglGetFrameTimestampsANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglQueryTimestampSupportedANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID },
};
/*
@@ -1196,7 +1203,7 @@
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 @@
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 @@
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 90f27d1..cfecf77 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -68,7 +68,7 @@
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 8f3b9cb..97eda4c 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -139,6 +139,7 @@
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 0000000..30337ad
--- /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 8f1eaf3..f0c024e 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -19,4 +19,11 @@
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 fb83eff..0c4dc26 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -435,15 +435,15 @@
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 c8de621..68d4154 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 2629794..133e5f1 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -359,6 +359,27 @@
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 b88e250..aa233df 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -137,15 +137,6 @@
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 @@
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 c0baa49..61bb0bd8 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -304,8 +304,11 @@
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 @@
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 @@
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 5b82355..bf9b39c 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -122,7 +122,6 @@
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 fd33d5c..36cfa37 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -127,10 +127,6 @@
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 71b6b59..f64fe51 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -57,7 +57,6 @@
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 c541668..5c7db2b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -462,6 +462,18 @@
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 @@
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 @@
// 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 @@
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::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 d84d4e1..69fb8c5 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -459,6 +459,18 @@
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 97a1e8b..543d0c7 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 @@
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();