Merge "Make sure constructors initialize all members"
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 1f56d21..3448e91 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -38,3 +38,11 @@
class main
disabled
oneshot
+
+# bugreportwear is a wearable version of bugreport that displays progress and takes early
+# screenshot.
+service bugreportwear /system/bin/dumpstate -d -B -P -p -z \
+ -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
+ class main
+ disabled
+ oneshot
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 e09c9a4..52dc833 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
@@ -60,6 +62,26 @@
static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
+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;
+
typedef int fd_t;
static bool property_get_bool(const char* property_name, bool default_value = false) {
@@ -77,30 +99,47 @@
return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME);
}
+static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid,
+ const char* pkgname, const char* seinfo) {
+ if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << path;
+ return -1;
+ }
+ if (selinux_android_setfilecon(path.c_str(), pkgname, seinfo, uid) < 0) {
+ PLOG(ERROR) << "Failed to setfilecon " << path;
+ return -1;
+ }
+ return 0;
+}
+
+static int prepare_app_dir(const std::string& parent, const char* name, mode_t target_mode,
+ uid_t uid, const char* pkgname, const char* seinfo) {
+ return prepare_app_dir(StringPrintf("%s/%s", parent.c_str(), name), target_mode, uid, pkgname,
+ seinfo);
+}
+
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) {
uid_t uid = multiuser_get_uid(userid, appid);
- int target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
+ mode_t target_mode = target_sdk_version >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
- if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
- PLOG(ERROR) << "Failed to prepare " << path;
+ if (prepare_app_dir(path, target_mode, uid, pkgname, seinfo) ||
+ prepare_app_dir(path, "cache", 0771, uid, pkgname, seinfo) ||
+ prepare_app_dir(path, "code_cache", 0771, uid, pkgname, seinfo)) {
return -1;
}
- if (selinux_android_setfilecon(path.c_str(), pkgname, seinfo, uid) < 0) {
- PLOG(ERROR) << "Failed to setfilecon " << path;
+
+ // Remember inode numbers of cache directories so that we can clear
+ // contents while CE storage is locked
+ if (write_path_inode(path, "cache", kXattrInodeCache) ||
+ write_path_inode(path, "code_cache", kXattrInodeCodeCache)) {
return -1;
}
}
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_package_path(uuid, userid, pkgname);
- if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) == -1) {
- PLOG(ERROR) << "Failed to prepare " << path;
- // TODO: include result once 25796509 is fixed
- return 0;
- }
- if (selinux_android_setfilecon(path.c_str(), pkgname, seinfo, uid) < 0) {
- PLOG(ERROR) << "Failed to setfilecon " << path;
+ if (prepare_app_dir(path, target_mode, uid, pkgname, seinfo)) {
// TODO: include result once 25796509 is fixed
return 0;
}
@@ -244,24 +283,29 @@
int clear_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
ino_t ce_data_inode) {
- std::string suffix = "";
- bool only_cache = false;
- if (flags & FLAG_CLEAR_CACHE_ONLY) {
- suffix = CACHE_DIR_POSTFIX;
- only_cache = true;
- } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
- suffix = CODE_CACHE_DIR_POSTFIX;
- only_cache = true;
- }
-
int res = 0;
if (flags & FLAG_STORAGE_CE) {
- auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode) + suffix;
+ auto path = create_data_user_ce_package_path(uuid, userid, pkgname, ce_data_inode);
+ if (flags & FLAG_CLEAR_CACHE_ONLY) {
+ path = read_path_inode(path, "cache", kXattrInodeCache);
+ } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+ path = read_path_inode(path, "code_cache", kXattrInodeCodeCache);
+ }
if (access(path.c_str(), F_OK) == 0) {
res |= delete_dir_contents(path);
}
}
if (flags & FLAG_STORAGE_DE) {
+ std::string suffix = "";
+ bool only_cache = false;
+ if (flags & FLAG_CLEAR_CACHE_ONLY) {
+ suffix = CACHE_DIR_POSTFIX;
+ only_cache = true;
+ } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
+ suffix = CODE_CACHE_DIR_POSTFIX;
+ only_cache = true;
+ }
+
auto path = create_data_user_de_package_path(uuid, userid, pkgname) + suffix;
if (access(path.c_str(), F_OK) == 0) {
// TODO: include result once 25796509 is fixed
@@ -605,14 +649,9 @@
}
int get_app_data_inode(const char *uuid, const char *pkgname, int userid, int flags, ino_t *inode) {
- struct stat buf;
- memset(&buf, 0, sizeof(buf));
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid, userid, pkgname);
- if (stat(path.c_str(), &buf) == 0) {
- *inode = buf.st_ino;
- return 0;
- }
+ return get_path_inode(path, inode);
}
return -1;
}
@@ -1427,7 +1466,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
@@ -2093,6 +2132,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;
@@ -2108,9 +2164,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 68439b2..2bc3dfa 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/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index c838993..ba0ce0a 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
+#include <sys/xattr.h>
#if defined(__APPLE__)
#include <sys/mount.h>
@@ -39,7 +40,9 @@
#ifndef LOG_TAG
#define LOG_TAG "installd"
#endif
+
#define CACHE_NOISY(x) //x
+#define DEBUG_XATTRS 0
using android::base::StringPrintf;
@@ -105,10 +108,12 @@
while ((ent = readdir(dir))) {
if (ent->d_ino == ce_data_inode) {
auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name);
+#if DEBUG_XATTRS
if (resolved != fallback) {
LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode
<< " instead of " << fallback;
}
+#endif
closedir(dir);
return resolved;
}
@@ -551,7 +556,7 @@
if (res == NULL) {
return NULL;
}
- CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %d", res, len));
+ CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %zu", res, len));
// Link it into our list of blocks, not disrupting the current one.
if (cache->memBlocks == NULL) {
*(void**)res = NULL;
@@ -576,7 +581,7 @@
cache->curMemBlockEnd = newBlock + CACHE_BLOCK_SIZE;
nextPos = res + len;
}
- CACHE_NOISY(ALOGI("cache_malloc: ret %p size %d, block=%p, nextPos=%p",
+ CACHE_NOISY(ALOGI("cache_malloc: ret %p size %zu, block=%p, nextPos=%p",
res, len, cache->memBlocks, nextPos));
cache->curMemBlockAvail = nextPos;
return res;
@@ -654,7 +659,7 @@
cache->availFiles = newAvail;
cache->files = newFiles;
}
- CACHE_NOISY(ALOGI("Setting file %p at position %d in array %p", file,
+ CACHE_NOISY(ALOGI("Setting file %p at position %zd in array %p", file,
cache->numFiles, cache->files));
cache->files[cache->numFiles] = file;
cache->numFiles++;
@@ -779,6 +784,99 @@
return 0;
}
+int get_path_inode(const std::string& path, ino_t *inode) {
+ struct stat buf;
+ memset(&buf, 0, sizeof(buf));
+ if (stat(path.c_str(), &buf) != 0) {
+ PLOG(WARNING) << "Failed to stat " << path;
+ return -1;
+ } else {
+ *inode = buf.st_ino;
+ return 0;
+ }
+}
+
+/**
+ * Write the inode of a specific child file into the given xattr on the
+ * parent directory. This allows you to find the child later, even if its
+ * name is encrypted.
+ */
+int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr) {
+ ino_t inode = 0;
+ uint64_t inode_raw = 0;
+ auto path = StringPrintf("%s/%s", parent.c_str(), name);
+
+ if (get_path_inode(path, &inode) != 0) {
+ // Path probably doesn't exist yet; ignore
+ return 0;
+ }
+
+ // Check to see if already set correctly
+ if (getxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) {
+ if (inode_raw == inode) {
+ // Already set correctly; skip writing
+ return 0;
+ } else {
+ PLOG(WARNING) << "Mismatched inode value; found " << inode
+ << " on disk but marked value was " << inode_raw << "; overwriting";
+ }
+ }
+
+ inode_raw = inode;
+ if (setxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw), 0) != 0) {
+ PLOG(ERROR) << "Failed to write xattr " << inode_xattr << " at " << parent;
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Read the inode of a specific child file from the given xattr on the
+ * parent directory. Returns a currently valid path for that child, which
+ * might have an encrypted name.
+ */
+std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr) {
+ ino_t inode = 0;
+ uint64_t inode_raw = 0;
+ auto fallback = StringPrintf("%s/%s", parent.c_str(), name);
+
+ // Lookup the inode value written earlier
+ if (getxattr(parent.c_str(), inode_xattr, &inode_raw, sizeof(inode_raw)) == sizeof(inode_raw)) {
+ inode = inode_raw;
+ }
+
+ // For testing purposes, rely on the inode when defined; this could be
+ // optimized to use access() in the future.
+ if (inode != 0) {
+ DIR* dir = opendir(parent.c_str());
+ if (dir == nullptr) {
+ PLOG(ERROR) << "Failed to opendir " << parent;
+ return fallback;
+ }
+
+ struct dirent* ent;
+ while ((ent = readdir(dir))) {
+ if (ent->d_ino == inode) {
+ auto resolved = StringPrintf("%s/%s", parent.c_str(), ent->d_name);
+#if DEBUG_XATTRS
+ if (resolved != fallback) {
+ LOG(DEBUG) << "Resolved path " << resolved << " for inode " << inode
+ << " instead of " << fallback;
+ }
+#endif
+ closedir(dir);
+ return resolved;
+ }
+ }
+ LOG(WARNING) << "Failed to resolve inode " << inode << "; using " << fallback;
+ closedir(dir);
+ return fallback;
+ } else {
+ return fallback;
+ }
+}
+
void add_cache_files(cache_t* cache, const std::string& data_path) {
DIR *d;
struct dirent *de;
@@ -796,7 +894,6 @@
if (de->d_type == DT_DIR) {
DIR* subdir;
const char *name = de->d_name;
- char* pathpos;
/* always skip "." and ".." */
if (name[0] == '.') {
@@ -804,16 +901,9 @@
if ((name[1] == '.') && (name[2] == 0)) continue;
}
- strcpy(dirname, basepath);
- pathpos = dirname + strlen(dirname);
- if ((*(pathpos-1)) != '/') {
- *pathpos = '/';
- pathpos++;
- *pathpos = 0;
- }
-
- // TODO: also try searching using xattr when CE is locked
- snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s/cache", name);
+ auto parent = StringPrintf("%s/%s", basepath, name);
+ auto resolved = read_path_inode(parent, "cache", kXattrInodeCache);
+ strcpy(dirname, resolved.c_str());
CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname));
subdir = opendir(dirname);
@@ -931,16 +1021,16 @@
{
CACHE_NOISY(size_t i;)
- CACHE_NOISY(ALOGI("clear_cache_files: %d dirs, %d files\n", cache->numDirs, cache->numFiles));
+ CACHE_NOISY(ALOGI("clear_cache_files: %zu dirs, %zu files\n", cache->numDirs, cache->numFiles));
CACHE_NOISY(
for (i=0; i<cache->numDirs; i++) {
cache_dir_t* dir = cache->dirs[i];
- ALOGI("dir #%d: %p %s parent=%p\n", i, dir, dir->name, dir->parent);
+ ALOGI("dir #%zu: %p %s parent=%p\n", i, dir, dir->name, dir->parent);
})
CACHE_NOISY(
for (i=0; i<cache->numFiles; i++) {
cache_file_t* file = cache->files[i];
- ALOGI("file #%d: %p %s time=%d dir=%p\n", i, file, file->name,
+ ALOGI("file #%zu: %p %s time=%d dir=%p\n", i, file, file->name,
(int)file->modTime, file->dir);
})
void* block = cache->memBlocks;
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 60df356..8123e9b 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -62,6 +62,9 @@
int8_t* curMemBlockEnd;
} cache_t;
+constexpr const char* kXattrInodeCache = "user.inode_cache";
+constexpr const char* kXattrInodeCodeCache = "user.inode_code_cache";
+
int create_pkg_path(char path[PKG_PATH_MAX],
const char *pkgname,
const char *postfix,
@@ -118,6 +121,11 @@
cache_t* start_cache_collection();
+int get_path_inode(const std::string& path, ino_t *inode);
+
+int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
+std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr);
+
void add_cache_files(cache_t* cache, const std::string& data_path);
void clear_cache_files(const std::string& data_path, cache_t* cache, int64_t free_size);
diff --git a/cmds/surfacecapturereplay/proto/Android.mk b/cmds/surfacecapturereplay/proto/Android.mk
new file mode 100644
index 0000000..871fb32
--- /dev/null
+++ b/cmds/surfacecapturereplay/proto/Android.mk
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-proto-files-under, src)
+
+LOCAL_SHARED_LIBRARIES := \
+ libprotobuf-cpp-full
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+
+LOCAL_MODULE := libtrace_proto
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/cmds/surfacecapturereplay/proto/src/trace.proto b/cmds/surfacecapturereplay/proto/src/trace.proto
new file mode 100644
index 0000000..ce08ecf
--- /dev/null
+++ b/cmds/surfacecapturereplay/proto/src/trace.proto
@@ -0,0 +1,136 @@
+syntax = "proto2";
+
+message Trace {
+ repeated Increment increment = 1;
+}
+
+message Increment {
+ required int64 time_stamp = 1;
+
+ oneof increment {
+ Transaction transaction = 2;
+ Create create = 3;
+ Delete delete = 4;
+ BufferUpdate buffer_update = 5;
+ VSyncEvent vsync_event = 6;
+ }
+}
+
+message Transaction {
+ repeated Change change = 1;
+
+ required bool synchronous = 2;
+ required bool animation = 3;
+}
+
+message Change {
+ required uint32 id = 1;
+
+ oneof Change {
+ PositionChange position = 2;
+ SizeChange size = 3;
+ AlphaChange alpha = 4;
+ LayerChange layer = 5;
+ CropChange crop = 6;
+ FinalCropChange final_crop = 7;
+ MatrixChange matrix = 8;
+ OverrideScalingModeChange override_scaling_mode = 9;
+ TransparentRegionHintChange transparent_region_hint = 10;
+ LayerStackChange layer_stack = 11;
+ HiddenFlagChange hidden_flag = 12;
+ OpaqueFlagChange opaque_flag = 13;
+ SecureFlagChange secure_flag = 14;
+ DeferredTransactionChange deferred_transaction = 15;
+ }
+}
+
+message PositionChange {
+ required float x = 1;
+ required float y = 2;
+}
+
+message SizeChange {
+ required uint32 w = 1;
+ required uint32 h = 2;
+}
+
+message AlphaChange {
+ required float alpha = 1;
+}
+
+message LayerChange {
+ required uint32 layer = 1;
+}
+
+message CropChange {
+ required Rectangle rectangle = 1;
+}
+
+message FinalCropChange {
+ required Rectangle rectangle = 1;
+}
+
+message MatrixChange {
+ required float dsdx = 1;
+ required float dtdx = 2;
+ required float dsdy = 3;
+ required float dtdy = 4;
+}
+
+message OverrideScalingModeChange {
+ required int32 override_scaling_mode = 1;
+}
+
+message TransparentRegionHintChange {
+ repeated Rectangle region = 1;
+}
+
+message LayerStackChange {
+ required uint32 layer_stack = 1;
+}
+
+message HiddenFlagChange {
+ required bool hidden_flag = 1;
+}
+
+message OpaqueFlagChange {
+ required bool opaque_flag = 1;
+}
+
+message SecureFlagChange {
+ required bool secure_flag = 1;
+}
+
+message DeferredTransactionChange {
+ required uint32 layer_id = 1;
+ required uint64 frame_number = 2;
+}
+
+message Rectangle {
+ required int32 left = 1;
+ required int32 top = 2;
+ required int32 right = 3;
+ required int32 bottom = 4;
+}
+
+message Create {
+ required uint32 id = 1;
+ required string name = 2;
+ required uint32 w = 3;
+ required uint32 h = 4;
+}
+
+message Delete {
+ required uint32 id = 1;
+}
+
+message BufferUpdate {
+ required uint32 id = 1;
+ required uint32 w = 2;
+ required uint32 h = 3;
+ required uint64 frame_number = 4;
+}
+
+message VSyncEvent {
+ required int64 when = 1;
+}
diff --git a/include/media/openmax/OMX_AsString.h b/include/media/openmax/OMX_AsString.h
index a7d1656..4c74bc5 100644
--- a/include/media/openmax/OMX_AsString.h
+++ b/include/media/openmax/OMX_AsString.h
@@ -544,6 +544,8 @@
case OMX_IndexParamVideoHevc: return "ParamVideoHevc";
// case OMX_IndexParamSliceSegments: return "ParamSliceSegments";
case OMX_IndexConfigAndroidIntraRefresh: return "ConfigAndroidIntraRefresh";
+ case OMX_IndexParamAndroidVideoTemporalLayering: return "ParamAndroidVideoTemporalLayering";
+ case OMX_IndexConfigAndroidVideoTemporalLayering: return "ConfigAndroidVideoTemporalLayering";
case OMX_IndexConfigAutoFramerateConversion: return "ConfigAutoFramerateConversion";
case OMX_IndexConfigPriority: return "ConfigPriority";
case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate";
@@ -974,8 +976,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;
}
}
@@ -1023,6 +1025,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_VideoExt.h b/include/media/openmax/OMX_VideoExt.h
index 6f3643e..128dd2d 100644
--- a/include/media/openmax/OMX_VideoExt.h
+++ b/include/media/openmax/OMX_VideoExt.h
@@ -137,7 +137,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];
@@ -247,7 +251,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 */
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 246346b..fb2b857 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -796,6 +796,11 @@
return NO_MEMORY;
}
+ if (numRects > (UINT32_MAX / sizeof(Rect))) {
+ android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, NULL, 0);
+ return NO_MEMORY;
+ }
+
Region result;
result.mStorage.clear();
for (size_t r = 0; r < numRects; ++r) {
diff --git a/opengl/libagl/arch-mips/fixed_asm.S b/opengl/libagl/arch-mips/fixed_asm.S
index e1a53bc..a30ffc5 100644
--- a/opengl/libagl/arch-mips/fixed_asm.S
+++ b/opengl/libagl/arch-mips/fixed_asm.S
@@ -17,7 +17,7 @@
.text
- .align
+ .align 4
/*
* this version rounds-to-nearest and saturates numbers
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 7a0cce4..fcb9357 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -54,6 +54,8 @@
// Implementation is incomplete and untested.
#define ENABLE_EGL_KHR_GL_COLORSPACE 0
+#define ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS 0
+
// ----------------------------------------------------------------------------
namespace android {
@@ -84,7 +86,9 @@
"EGL_KHR_swap_buffers_with_damage "
"EGL_ANDROID_create_native_client_buffer "
"EGL_ANDROID_front_buffer_auto_refresh "
+#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
"EGL_ANDROID_get_frame_timestamps "
+#endif
;
extern char const * const gExtensionString =
"EGL_KHR_image " // mandatory
@@ -1212,10 +1216,12 @@
setError(EGL_BAD_SURFACE, EGL_FALSE);
}
+#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
if (attribute == EGL_TIMESTAMPS_ANDROID) {
s->enableTimestamps = value;
return EGL_TRUE;
}
+#endif
if (s->cnx->egl.eglSurfaceAttrib) {
return s->cnx->egl.eglSurfaceAttrib(
@@ -2036,6 +2042,7 @@
}
switch (timestamp) {
+#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
case EGL_QUEUE_TIME_ANDROID:
case EGL_RENDERING_COMPLETE_TIME_ANDROID:
case EGL_COMPOSITION_START_TIME_ANDROID:
@@ -2043,6 +2050,7 @@
case EGL_DISPLAY_RETIRE_TIME_ANDROID:
case EGL_READS_DONE_TIME_ANDROID:
return EGL_TRUE;
+#endif
default:
return EGL_FALSE;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index ed8cc08..87a0e9a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -149,7 +149,12 @@
}
for (auto element : mDisplays) {
- auto display = element.second;
+ auto display = element.second.lock();
+ if (!display) {
+ ALOGE("~Device: Found a display (%" PRId64 " that has already been"
+ " destroyed", element.first);
+ continue;
+ }
DisplayType displayType = HWC2::DisplayType::Invalid;
auto error = display->getType(&displayType);
@@ -208,6 +213,10 @@
ALOGI("Created virtual display");
*format = static_cast<android_pixel_format_t>(intFormat);
*outDisplay = getDisplayById(displayId);
+ if (!*outDisplay) {
+ ALOGE("Failed to get display by id");
+ return Error::BadDisplay;
+ }
(*outDisplay)->setVirtual();
return Error::None;
}
@@ -289,7 +298,10 @@
std::shared_ptr<Display> Device::getDisplayById(hwc2_display_t id) {
if (mDisplays.count(id) != 0) {
- return mDisplays.at(id);
+ auto strongDisplay = mDisplays[id].lock();
+ ALOGE_IF(!strongDisplay, "Display %" PRId64 " is in mDisplays but is no"
+ " longer alive", id);
+ return strongDisplay;
}
auto display = std::make_shared<Display>(*this, id);
@@ -305,9 +317,12 @@
"Capability size has changed");
uint32_t numCapabilities = 0;
mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, nullptr);
- mCapabilities.resize(numCapabilities);
- auto asInt = reinterpret_cast<int32_t*>(mCapabilities.data());
+ std::vector<Capability> capabilities(numCapabilities);
+ auto asInt = reinterpret_cast<int32_t*>(capabilities.data());
mHwcDevice->getCapabilities(mHwcDevice, &numCapabilities, asInt);
+ for (auto capability : capabilities) {
+ mCapabilities.emplace(capability);
+ }
}
bool Device::hasCapability(HWC2::Capability capability) const
@@ -430,6 +445,7 @@
auto error = static_cast<Error>(intError);
ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64 ") failed:"
" %s (%d)", display, to_string(error).c_str(), intError);
+ mDisplays.erase(display);
}
// Display methods
@@ -810,6 +826,7 @@
auto handle = buffer->getNativeBuffer()->handle;
int32_t intError = mDevice.mSetOutputBuffer(mDevice.mHwcDevice, mId, handle,
fenceFd);
+ close(fenceFd);
return static_cast<Error>(intError);
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 8ab61e9..beb7bc6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -33,6 +33,7 @@
#include <functional>
#include <string>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
namespace android {
@@ -66,7 +67,7 @@
std::string dump() const;
- const std::vector<Capability>& getCapabilities() const {
+ const std::unordered_set<Capability>& getCapabilities() const {
return mCapabilities;
};
@@ -88,7 +89,8 @@
// Other Device methods
// This will create a Display if one is not found, but it will not be marked
- // as connected
+ // as connected. This Display may be null if the display has been torn down
+ // but has not been removed from the map yet.
std::shared_ptr<Display> getDisplayById(hwc2_display_t id);
bool hasCapability(HWC2::Capability capability) const;
@@ -180,8 +182,8 @@
HWC2_PFN_SET_LAYER_VISIBLE_REGION mSetLayerVisibleRegion;
HWC2_PFN_SET_LAYER_Z_ORDER mSetLayerZOrder;
- std::vector<Capability> mCapabilities;
- std::unordered_map<hwc2_display_t, std::shared_ptr<Display>> mDisplays;
+ std::unordered_set<Capability> mCapabilities;
+ std::unordered_map<hwc2_display_t, std::weak_ptr<Display>> mDisplays;
HotplugCallback mHotplug;
std::vector<std::pair<std::shared_ptr<Display>, Connection>>
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 2629794..199848f 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -144,6 +144,11 @@
mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}
+bool HWComposer::hasCapability(HWC2::Capability capability) const
+{
+ return mHwcDevice->getCapabilities().count(capability) > 0;
+}
+
bool HWComposer::isValidDisplay(int32_t displayId) const {
return static_cast<size_t>(displayId) < mDisplayData.size() &&
mDisplayData[displayId].hwcDisplay;
@@ -359,6 +364,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);
@@ -671,6 +697,28 @@
return NO_ERROR;
}
+status_t HWComposer::setColorTransform(int32_t displayId,
+ const mat4& transform) {
+ if (!isValidDisplay(displayId)) {
+ ALOGE("setColorTransform: Display %d is not valid", displayId);
+ return BAD_INDEX;
+ }
+
+ auto& displayData = mDisplayData[displayId];
+ bool isIdentity = transform == mat4();
+ auto error = displayData.hwcDisplay->setColorTransform(transform,
+ isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY :
+ HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX);
+ if (error != HWC2::Error::None) {
+ ALOGE("setColorTransform: Failed to set transform on display %d: "
+ "%s (%d)", displayId, to_string(error).c_str(),
+ static_cast<int32_t>(error));
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
void HWComposer::disconnectDisplay(int displayId) {
LOG_ALWAYS_FATAL_IF(displayId < 0);
auto& displayData = mDisplayData[displayId];
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b88e250..17676ae 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -81,6 +81,8 @@
void setEventHandler(EventHandler* handler);
+ bool hasCapability(HWC2::Capability capability) const;
+
// Attempts to allocate a virtual display. If the virtual display is created
// on the HWC device, outId will contain its HWC ID.
status_t allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -104,6 +106,9 @@
// set active config
status_t setActiveConfig(int32_t displayId, size_t configId);
+ // Sets a color transform to be applied to the result of composition
+ status_t setColorTransform(int32_t displayId, const mat4& transform);
+
// reset state when an external, non-virtual display is disconnected
void disconnectDisplay(int32_t displayId);
@@ -137,15 +142,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 +154,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/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index feb8936..a104e8f 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -19,21 +19,14 @@
namespace android {
-Daltonizer::Daltonizer() :
- mType(deuteranomaly), mMode(simulation), mDirty(true) {
-}
-
-Daltonizer::~Daltonizer() {
-}
-
-void Daltonizer::setType(Daltonizer::ColorBlindnessTypes type) {
+void Daltonizer::setType(ColorBlindnessType type) {
if (type != mType) {
mDirty = true;
mType = type;
}
}
-void Daltonizer::setMode(Daltonizer::Mode mode) {
+void Daltonizer::setMode(ColorBlindnessMode mode) {
if (mode != mMode) {
mDirty = true;
mMode = mode;
@@ -49,6 +42,11 @@
}
void Daltonizer::update() {
+ if (mType == ColorBlindnessType::None) {
+ mColorTransform = mat4();
+ return;
+ }
+
// converts a linear RGB color to the XYZ space
const mat4 rgb2xyz( 0.4124, 0.2126, 0.0193, 0,
0.3576, 0.7152, 0.1192, 0,
@@ -149,24 +147,25 @@
mat4 correction(0);
switch (mType) {
- case protanopia:
- case protanomaly:
+ case ColorBlindnessType::Protanomaly:
simulation = lms2lmsp;
- if (mMode == Daltonizer::correction)
+ if (mMode == ColorBlindnessMode::Correction)
correction = errp;
break;
- case deuteranopia:
- case deuteranomaly:
+ case ColorBlindnessType::Deuteranomaly:
simulation = lms2lmsd;
- if (mMode == Daltonizer::correction)
+ if (mMode == ColorBlindnessMode::Correction)
correction = errd;
break;
- case tritanopia:
- case tritanomaly:
+ case ColorBlindnessType::Tritanomaly:
simulation = lms2lmst;
- if (mMode == Daltonizer::correction)
+ if (mMode == ColorBlindnessMode::Correction)
correction = errt;
break;
+ case ColorBlindnessType::None:
+ // We already caught this at the beginning of the method, but the
+ // compiler doesn't know that
+ break;
}
mColorTransform = lms2rgb *
diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h
index e816437..d21b155 100644
--- a/services/surfaceflinger/Effects/Daltonizer.h
+++ b/services/surfaceflinger/Effects/Daltonizer.h
@@ -21,27 +21,22 @@
namespace android {
+enum class ColorBlindnessType {
+ None, // Disables the Daltonizer
+ Protanomaly, // L (red) cone deficient
+ Deuteranomaly, // M (green) cone deficient (most common)
+ Tritanomaly // S (blue) cone deficient
+};
+
+enum class ColorBlindnessMode {
+ Simulation,
+ Correction
+};
+
class Daltonizer {
public:
- enum ColorBlindnessTypes {
- protanopia, // L (red) cone missing
- deuteranopia, // M (green) cone missing
- tritanopia, // S (blue) cone missing
- protanomaly, // L (red) cone deficient
- deuteranomaly, // M (green) cone deficient (most common)
- tritanomaly // S (blue) cone deficient
- };
-
- enum Mode {
- simulation,
- correction
- };
-
- Daltonizer();
- ~Daltonizer();
-
- void setType(ColorBlindnessTypes type);
- void setMode(Mode mode);
+ void setType(ColorBlindnessType type);
+ void setMode(ColorBlindnessMode mode);
// returns the color transform to apply in the shader
const mat4& operator()();
@@ -49,9 +44,9 @@
private:
void update();
- ColorBlindnessTypes mType;
- Mode mMode;
- bool mDirty;
+ ColorBlindnessType mType = ColorBlindnessType::None;
+ ColorBlindnessMode mMode = ColorBlindnessMode::Simulation;
+ bool mDirty = true;
mat4 mColorTransform;
};
diff --git a/services/surfaceflinger/GpuService.cpp b/services/surfaceflinger/GpuService.cpp
index b981180..b993dfb 100644
--- a/services/surfaceflinger/GpuService.cpp
+++ b/services/surfaceflinger/GpuService.cpp
@@ -92,82 +92,27 @@
}
fprintf(outs,
"GPU Service commands:\n"
- " vkjson dump Vulkan device capabilities as JSON\n");
+ " vkjson dump Vulkan properties as JSON\n");
fclose(outs);
return NO_ERROR;
}
-VkResult vkjsonPrint(FILE* out, FILE* err) {
- VkResult result;
-
- const VkApplicationInfo app_info = {
- VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr,
- "vkjson", 1, /* app name, version */
- "", 0, /* engine name, version */
- VK_API_VERSION_1_0
- };
- const VkInstanceCreateInfo instance_info = {
- VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, nullptr,
- 0, /* flags */
- &app_info,
- 0, nullptr, /* layers */
- 0, nullptr, /* extensions */
- };
- VkInstance instance;
- result = vkCreateInstance(&instance_info, nullptr, &instance);
- if (result != VK_SUCCESS) {
- fprintf(err, "vkCreateInstance failed: %d\n", result);
- return result;
- }
-
- uint32_t ngpu = 0;
- result = vkEnumeratePhysicalDevices(instance, &ngpu, nullptr);
- if (result != VK_SUCCESS) {
- fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
- return result;
- }
- std::vector<VkPhysicalDevice> gpus(ngpu, VK_NULL_HANDLE);
- result = vkEnumeratePhysicalDevices(instance, &ngpu, gpus.data());
- if (result != VK_SUCCESS) {
- fprintf(err, "vkEnumeratePhysicalDevices failed: %d\n", result);
- return result;
- }
-
- for (size_t i = 0, n = gpus.size(); i < n; i++) {
- auto props = VkJsonGetAllProperties(gpus[i]);
- std::string json = VkJsonAllPropertiesToJson(props);
- fwrite(json.data(), 1, json.size(), out);
- if (i < n - 1)
- fputc(',', out);
- fputc('\n', out);
- }
-
- vkDestroyInstance(instance, nullptr);
-
- return VK_SUCCESS;
+void vkjsonPrint(FILE* out) {
+ std::string json = VkJsonInstanceToJson(VkJsonGetInstance());
+ fwrite(json.data(), 1, json.size(), out);
+ fputc('\n', out);
}
-status_t cmd_vkjson(int out, int err) {
- int errnum;
+status_t cmd_vkjson(int out, int /*err*/) {
FILE* outs = fdopen(out, "w");
if (!outs) {
- errnum = errno;
+ int errnum = errno;
ALOGE("vkjson: failed to create output stream: %s", strerror(errnum));
return -errnum;
}
- FILE* errs = fdopen(err, "w");
- if (!errs) {
- errnum = errno;
- ALOGE("vkjson: failed to create error stream: %s", strerror(errnum));
- fclose(outs);
- return -errnum;
- }
- fprintf(outs, "[\n");
- VkResult result = vkjsonPrint(outs, errs);
- fprintf(outs, "]\n");
- fclose(errs);
+ vkjsonPrint(outs);
fclose(outs);
- return result >= 0 ? NO_ERROR : UNKNOWN_ERROR;
+ return NO_ERROR;
}
} // anonymous namespace
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d985b3b..5583f0a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -90,6 +90,19 @@
EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+// Workaround for b/30067360: /proc/self/environ inaccessible in SurfaceFlinger
+// => ASan fails to read ASAN_OPTIONS => alloc-dealloc-mismatch bug is not
+// suppressed and prevents the device from booting.
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+#if __has_feature(address_sanitizer)
+__attribute__((visibility("default")))
+extern "C" const char* __asan_default_options() {
+ return "alloc_dealloc_mismatch=0";
+}
+#endif
+
namespace android {
// This is the phase offset in nanoseconds of the software vsync event
@@ -152,7 +165,6 @@
mPrimaryDispSync("PrimaryDispSync"),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
- mDaltonize(false),
mHasColorMatrix(false),
mHasPoweredOff(false),
mFrameBuckets(),
@@ -614,9 +626,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
@@ -635,7 +644,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;
@@ -1216,8 +1236,7 @@
}
layer->setGeometry(displayDevice);
- if (mDebugDisableHWC || mDebugRegion || mDaltonize ||
- mHasColorMatrix) {
+ if (mDebugDisableHWC || mDebugRegion) {
layer->forceClientComposition(hwcId);
}
}
@@ -1225,6 +1244,9 @@
}
}
+
+ mat4 colorMatrix = mColorMatrix * mDaltonizer();
+
// Set the per-frame data
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
@@ -1232,11 +1254,18 @@
if (hwcId < 0) {
continue;
}
+ if (colorMatrix != mPreviousColorMatrix) {
+ status_t result = mHwc->setColorTransform(hwcId, colorMatrix);
+ ALOGE_IF(result != NO_ERROR, "Failed to set color transform on "
+ "display %zd: %d", displayId, result);
+ }
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
layer->setPerFrameData(displayDevice);
}
}
+ mPreviousColorMatrix = colorMatrix;
+
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
if (!displayDevice->isDisplayOn()) {
@@ -1918,18 +1947,7 @@
}
}
- if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) {
- if (!doComposeSurfaces(hw, dirtyRegion)) return;
- } else {
- RenderEngine& engine(getRenderEngine());
- mat4 colorMatrix = mColorMatrix;
- if (mDaltonize) {
- colorMatrix = colorMatrix * mDaltonizer();
- }
- mat4 oldMatrix = engine.setupColorTransform(colorMatrix);
- doComposeSurfaces(hw, dirtyRegion);
- engine.setupColorTransform(oldMatrix);
- }
+ if (!doComposeSurfaces(hw, dirtyRegion)) return;
// update the swap region and clear the dirty region
hw->swapRegion.orSelf(dirtyRegion);
@@ -1944,6 +1962,15 @@
ALOGV("doComposeSurfaces");
const auto hwcId = displayDevice->getHwcDisplayId();
+
+ mat4 oldColorMatrix;
+ const bool applyColorMatrix = !mHwc->hasDeviceComposition(hwcId) &&
+ !mHwc->hasCapability(HWC2::Capability::SkipClientColorTransform);
+ if (applyColorMatrix) {
+ mat4 colorMatrix = mColorMatrix * mDaltonizer();
+ oldColorMatrix = getRenderEngine().setupColorTransform(colorMatrix);
+ }
+
bool hasClientComposition = mHwc->hasClientComposition(hwcId);
if (hasClientComposition) {
ALOGV("hasClientComposition");
@@ -2061,6 +2088,10 @@
}
}
+ if (applyColorMatrix) {
+ getRenderEngine().setupColorTransform(oldColorMatrix);
+ }
+
// disable scissor at the end of the frame
mRenderEngine->disableScissor();
return true;
@@ -2936,8 +2967,7 @@
colorizer.bold(result);
result.append("h/w composer state:\n");
colorizer.reset(result);
- bool hwcDisabled = mDebugDisableHWC || mDebugRegion || mDaltonize ||
- mHasColorMatrix;
+ bool hwcDisabled = mDebugDisableHWC || mDebugRegion;
result.appendFormat(" h/w composer %s\n",
hwcDisabled ? "disabled" : "enabled");
hwc.dump(result);
@@ -3092,16 +3122,24 @@
// daltonize
n = data.readInt32();
switch (n % 10) {
- case 1: mDaltonizer.setType(Daltonizer::protanomaly); break;
- case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break;
- case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break;
+ case 1:
+ mDaltonizer.setType(ColorBlindnessType::Protanomaly);
+ break;
+ case 2:
+ mDaltonizer.setType(ColorBlindnessType::Deuteranomaly);
+ break;
+ case 3:
+ mDaltonizer.setType(ColorBlindnessType::Tritanomaly);
+ break;
+ default:
+ mDaltonizer.setType(ColorBlindnessType::None);
+ break;
}
if (n >= 10) {
- mDaltonizer.setMode(Daltonizer::correction);
+ mDaltonizer.setMode(ColorBlindnessMode::Correction);
} else {
- mDaltonizer.setMode(Daltonizer::simulation);
+ mDaltonizer.setMode(ColorBlindnessMode::Simulation);
}
- mDaltonize = n > 0;
invalidateHwcGeometry();
repaintEverything();
return NO_ERROR;
@@ -3109,15 +3147,14 @@
case 1015: {
// apply a color matrix
n = data.readInt32();
- mHasColorMatrix = n ? 1 : 0;
if (n) {
// color matrix is sent as mat3 matrix followed by vec3
// offset, then packed into a mat4 where the last row is
// the offset and extra values are 0
for (size_t i = 0 ; i < 4; i++) {
- for (size_t j = 0; j < 4; j++) {
- mColorMatrix[i][j] = data.readFloat();
- }
+ for (size_t j = 0; j < 4; j++) {
+ mColorMatrix[i][j] = data.readFloat();
+ }
}
} else {
mColorMatrix = mat4();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0df39a4..f063aaf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -527,8 +527,11 @@
*/
Daltonizer mDaltonizer;
+#ifndef USE_HWC2
bool mDaltonize;
+#endif
+ mat4 mPreviousColorMatrix;
mat4 mColorMatrix;
bool mHasColorMatrix;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index 868280d..4ddad94 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -90,6 +90,19 @@
EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+// Workaround for b/30067360: /proc/self/environ inaccessible in SurfaceFlinger
+// => ASan fails to read ASAN_OPTIONS => alloc-dealloc-mismatch bug is not
+// suppressed and prevents the device from booting.
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+#if __has_feature(address_sanitizer)
+__attribute__((visibility("default")))
+extern "C" const char *__asan_default_options() {
+ return "alloc_dealloc_mismatch=0";
+}
+#endif
+
namespace android {
// This is the phase offset in nanoseconds of the software vsync event
@@ -3080,14 +3093,20 @@
// daltonize
n = data.readInt32();
switch (n % 10) {
- case 1: mDaltonizer.setType(Daltonizer::protanomaly); break;
- case 2: mDaltonizer.setType(Daltonizer::deuteranomaly); break;
- case 3: mDaltonizer.setType(Daltonizer::tritanomaly); break;
+ case 1:
+ mDaltonizer.setType(ColorBlindnessType::Protanomaly);
+ break;
+ case 2:
+ mDaltonizer.setType(ColorBlindnessType::Deuteranomaly);
+ break;
+ case 3:
+ mDaltonizer.setType(ColorBlindnessType::Tritanomaly);
+ break;
}
if (n >= 10) {
- mDaltonizer.setMode(Daltonizer::correction);
+ mDaltonizer.setMode(ColorBlindnessMode::Correction);
} else {
- mDaltonizer.setMode(Daltonizer::simulation);
+ mDaltonizer.setMode(ColorBlindnessMode::Simulation);
}
mDaltonize = n > 0;
invalidateHwcGeometry();