Merge "GpuService: Dump all instance info, not just devices"
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index ca7d574..c33fc1f 100644
--- a/cmds/dumpstate/bugreport-format.md
+++ b/cmds/dumpstate/bugreport-format.md
@@ -22,7 +22,7 @@
file as the `ACTION_SEND_MULTIPLE` attachment.
## Version 1.0 (Android N)
-On _Android N (TBD)_, `dumpstate` generates a zip file directly (unless there
+On _Android N (Nougat)_, `dumpstate` generates a zip file directly (unless there
is a failure, in which case it reverts to the flat file that is zipped by
**Shell** and hence the end result is the _v0_ format).
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 800592b..bf99017 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -67,11 +67,14 @@
static std::string suffix;
#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
+#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
#define RAFT_DIR "/data/misc/raft"
#define RECOVERY_DIR "/cache/recovery"
#define RECOVERY_DATA_DIR "/data/misc/recovery"
#define LOGPERSIST_DATA_DIR "/data/misc/logd"
+#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
+#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
#define TOMBSTONE_DIR "/data/tombstones"
#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
/* Can accomodate a tombstone number up to 9999. */
@@ -563,18 +566,39 @@
printf("\n");
}
+// List of file extensions that can cause a zip file attachment to be rejected by some email
+// service providers.
+static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
+ ".ade", ".adp", ".bat", ".chm", ".cmd", ".com", ".cpl", ".exe", ".hta", ".ins", ".isp",
+ ".jar", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msp", ".mst", ".pif", ".scr", ".sct",
+ ".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
+};
+
bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
if (!zip_writer) {
MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n",
entry_name.c_str());
return false;
}
+ std::string valid_name = entry_name;
+
+ // Rename extension if necessary.
+ size_t idx = entry_name.rfind(".");
+ if (idx != std::string::npos) {
+ std::string extension = entry_name.substr(idx);
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if (PROBLEMATIC_FILE_EXTENSIONS.count(extension) != 0) {
+ valid_name = entry_name + ".renamed";
+ MYLOGI("Renaming entry %s to %s\n", entry_name.c_str(), valid_name.c_str());
+ }
+ }
+
// Logging statement below is useful to time how long each entry takes, but it's too verbose.
// MYLOGD("Adding zip entry %s\n", entry_name.c_str());
- int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(),
+ int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(),
ZipWriter::kCompress, get_mtime(fd, now));
if (err) {
- MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+ MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
ZipWriter::ErrorCodeString(err));
return false;
}
@@ -619,6 +643,7 @@
return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
}
+// TODO: move to util.cpp
void add_dir(const char *dir, bool recursive) {
if (!zip_writer) {
MYLOGD("Not adding dir %s because zip_writer is not set\n", dir);
@@ -659,6 +684,15 @@
return true;
}
+static void dump_iptables() {
+ run_command("IPTABLES", 10, "iptables", "-L", "-nvx", NULL);
+ run_command("IP6TABLES", 10, "ip6tables", "-L", "-nvx", NULL);
+ run_command("IPTABLE NAT", 10, "iptables", "-t", "nat", "-L", "-nvx", NULL);
+ /* no ip6 nat */
+ run_command("IPTABLE RAW", 10, "iptables", "-t", "raw", "-L", "-nvx", NULL);
+ run_command("IP6TABLE RAW", 10, "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
+}
+
static void dumpstate(const std::string& screenshot_path, const std::string& version) {
DurationReporter duration_reporter("DUMPSTATE");
unsigned long timeout;
@@ -809,6 +843,8 @@
if (!stat(PSTORE_LAST_KMSG, &st)) {
/* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
dump_file("LAST KMSG", PSTORE_LAST_KMSG);
+ } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
+ dump_file("LAST KMSG", ALT_PSTORE_LAST_KMSG);
} else {
/* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
dump_file("LAST KMSG", "/proc/last_kmsg");
@@ -837,16 +873,7 @@
run_command("ARP CACHE", 10, "ip", "-4", "neigh", "show", NULL);
run_command("IPv6 ND CACHE", 10, "ip", "-6", "neigh", "show", NULL);
run_command("MULTICAST ADDRESSES", 10, "ip", "maddr", NULL);
-
- run_command("IPTABLES", 10, SU_PATH, "root", "iptables", "-L", "-nvx", NULL);
- run_command("IP6TABLES", 10, SU_PATH, "root", "ip6tables", "-L", "-nvx", NULL);
- run_command("IPTABLE NAT", 10, SU_PATH, "root", "iptables", "-t", "nat", "-L", "-nvx", NULL);
- /* no ip6 nat */
- run_command("IPTABLE RAW", 10, SU_PATH, "root", "iptables", "-t", "raw", "-L", "-nvx", NULL);
- run_command("IP6TABLE RAW", 10, SU_PATH, "root", "ip6tables", "-t", "raw", "-L", "-nvx", NULL);
-
- run_command("WIFI NETWORKS", 20,
- SU_PATH, "root", "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
+ run_command("WIFI NETWORKS", 20, "wpa_cli", "IFNAME=wlan0", "list_networks", NULL);
#ifdef FWDUMP_bcmdhd
run_command("ND OFFLOAD TABLE", 5,
@@ -1109,10 +1136,18 @@
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
- FILE *oom_adj = fopen("/proc/self/oom_adj", "we");
+
+ FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
if (oom_adj) {
- fputs("-17", oom_adj);
+ fputs("-1000", oom_adj);
fclose(oom_adj);
+ } else {
+ /* fallback to kernels <= 2.6.35 */
+ oom_adj = fopen("/proc/self/oom_adj", "we");
+ if (oom_adj) {
+ fputs("-17", oom_adj);
+ fclose(oom_adj);
+ }
}
/* parse arguments */
@@ -1334,12 +1369,17 @@
/* collect stack traces from Dalvik and native processes (needs root) */
dump_traces_path = dump_traces();
- /* Get the tombstone fds, recovery files, and mount info here while we are running as root. */
+ /* Run some operations that require root. */
get_tombstone_fds(tombstone_data);
add_dir(RECOVERY_DIR, true);
add_dir(RECOVERY_DATA_DIR, true);
add_dir(LOGPERSIST_DATA_DIR, false);
+ if (!is_user_build()) {
+ add_dir(PROFILE_DATA_DIR_CUR, true);
+ add_dir(PROFILE_DATA_DIR_REF, true);
+ }
add_mountinfo();
+ dump_iptables();
if (!drop_root_user()) {
return -1;
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 90ccbf8..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;
}
@@ -755,6 +794,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 +904,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 +948,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 +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
@@ -2079,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;
@@ -2094,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/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/android/keycodes.h b/include/android/keycodes.h
index a17c57a..e202060 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -759,13 +759,13 @@
/** Paste key. */
AKEYCODE_PASTE = 279,
/** fingerprint navigation key, up. */
- AKEYCODE_FP_NAV_UP = 280,
+ AKEYCODE_SYSTEM_NAVIGATION_UP = 280,
/** fingerprint navigation key, down. */
- AKEYCODE_FP_NAV_DOWN = 281,
+ AKEYCODE_SYSTEM_NAVIGATION_DOWN = 281,
/** fingerprint navigation key, left. */
- AKEYCODE_FP_NAV_LEFT = 282,
+ AKEYCODE_SYSTEM_NAVIGATION_LEFT = 282,
/** fingerprint navigation key, right. */
- AKEYCODE_FP_NAV_RIGHT = 283
+ AKEYCODE_SYSTEM_NAVIGATION_RIGHT = 283
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index 6c69d69..98add9e 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -325,6 +325,8 @@
OccupancyTracker mOccupancyTracker;
+ const uint64_t mUniqueId;
+
}; // class BufferQueueCore
} // namespace android
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index a85bbb7..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;
@@ -190,6 +187,9 @@
virtual bool getFrameTimestamps(uint64_t frameNumber,
FrameTimestamps* outTimestamps) const override;
+ // See IGraphicBufferProducer::getUniqueId
+ virtual status_t getUniqueId(uint64_t* outId) const override;
+
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 0c24606..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
@@ -578,6 +580,9 @@
// If a fence has not yet signaled the timestamp returned will be 0;
virtual bool getFrameTimestamps(uint64_t /*frameNumber*/,
FrameTimestamps* /*outTimestamps*/) const { return false; }
+
+ // Returns a unique id for this BufferQueue
+ virtual status_t getUniqueId(uint64_t* outId) const = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/ISurfaceComposerClient.h b/include/gui/ISurfaceComposerClient.h
index bb79bd0..c27a741 100644
--- a/include/gui/ISurfaceComposerClient.h
+++ b/include/gui/ISurfaceComposerClient.h
@@ -77,6 +77,9 @@
* Requires ACCESS_SURFACE_FLINGER permission
*/
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
+
+ virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
+ bool* outTransformToDisplayInverse) const = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/Sensor.h b/include/gui/Sensor.h
index 3f60741..094fd16 100644
--- a/include/gui/Sensor.h
+++ b/include/gui/Sensor.h
@@ -89,7 +89,14 @@
bool isDynamicSensor() const;
bool hasAdditionalInfo() const;
int32_t getReportingMode() const;
+
+ // Note that after setId() has been called, getUuid() no longer
+ // returns the UUID.
+ // TODO(b/29547335): Remove getUuid(), add getUuidIndex(), and
+ // make sure setId() doesn't change the UuidIndex.
const uuid_t& getUuid() const;
+ int32_t getId() const;
+ void setId(int32_t id);
// LightFlattenable protocol
inline bool isFixedSize() const { return false; }
@@ -116,6 +123,9 @@
int32_t mRequiredAppOp;
int32_t mMaxDelay;
uint32_t mFlags;
+ // TODO(b/29547335): Get rid of this field and replace with an index.
+ // The index will be into a separate global vector of UUIDs.
+ // Also add an mId field (and change flatten/unflatten appropriately).
uuid_t mUuid;
static void flattenString8(void*& buffer, size_t& size, const String8& string8);
static bool unflattenString8(void const*& buffer, size_t& size, String8& outputString8);
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 7d9d901..8177ec6 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -140,6 +140,8 @@
nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayRetireTime,
nsecs_t* outReleaseTime);
+ status_t getUniqueId(uint64_t* outId) const;
+
protected:
virtual ~Surface();
@@ -196,7 +198,6 @@
virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
virtual int perform(int operation, va_list args);
- virtual int query(int what, int* value) const;
virtual int setSwapInterval(int interval);
virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer);
@@ -222,6 +223,7 @@
virtual int setAutoRefresh(bool autoRefresh);
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
+ virtual int query(int what, int* value) const;
virtual int connect(int api, const sp<IProducerListener>& listener);
virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer,
@@ -368,7 +370,13 @@
// used to prevent a mismatch between the number of queue/dequeue calls.
bool mSharedBufferHasBeenQueued;
+ // These are used to satisfy the NATIVE_WINDOW_LAST_*_DURATION queries
+ nsecs_t mLastDequeueDuration = 0;
+ nsecs_t mLastQueueDuration = 0;
+
Condition mQueueBufferCondition;
+
+ uint64_t mNextFrameNumber;
};
namespace view {
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index 312e02f..95e8b70 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -140,13 +140,16 @@
const sp<IBinder>& handle, uint64_t frameNumber);
status_t setOverrideScalingMode(const sp<IBinder>& id,
int32_t overrideScalingMode);
- status_t setPositionAppliesWithResize(const sp<IBinder>& id);
+ status_t setGeometryAppliesWithResize(const sp<IBinder>& id);
status_t destroySurface(const sp<IBinder>& id);
status_t clearLayerFrameStats(const sp<IBinder>& token) const;
status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const;
+ status_t getTransformToDisplayInverse(const sp<IBinder>& token,
+ bool* outTransformToDisplayInverse) const;
+
static status_t clearAnimationFrameStats();
static status_t getAnimationFrameStats(FrameStats* outStats);
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index fafd194..5e731c3 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -73,10 +73,11 @@
status_t setCrop(const Rect& crop);
status_t setFinalCrop(const Rect& crop);
- // If the size changes in this transaction, position updates specified
+ // If the size changes in this transaction, all geometry updates specified
// in this transaction will not complete until a buffer of the new size
- // arrives.
- status_t setPositionAppliesWithResize();
+ // arrives. As some elements normally apply immediately, this enables
+ // freezing the total geometry of a surface until a resize is completed.
+ status_t setGeometryAppliesWithResize();
// Defers applying any changes made in this transaction until the Layer
// identified by handle reaches the given frameNumber
@@ -96,6 +97,8 @@
status_t clearLayerFrameStats() const;
status_t getLayerFrameStats(FrameStats* outStats) const;
+ status_t getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const;
+
private:
// can't be copied
SurfaceControl& operator = (SurfaceControl& rhs);
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 542f647..0bd14ea 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -319,10 +319,10 @@
DEFINE_KEYCODE(CUT),
DEFINE_KEYCODE(COPY),
DEFINE_KEYCODE(PASTE),
- DEFINE_KEYCODE(FP_NAV_UP),
- DEFINE_KEYCODE(FP_NAV_DOWN),
- DEFINE_KEYCODE(FP_NAV_LEFT),
- DEFINE_KEYCODE(FP_NAV_RIGHT),
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP),
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN),
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT),
+ DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
{ NULL, 0 }
};
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_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index afd47b3..78d1f5d 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -80,6 +80,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 4b26892..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 */
@@ -309,7 +316,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;
@@ -317,6 +324,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/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 4885e05..4b3fcc6 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -55,7 +55,7 @@
eDeferTransaction = 0x00000200,
eFinalCropChanged = 0x00000400,
eOverrideScalingModeChanged = 0x00000800,
- ePositionAppliesWithResize = 0x00001000,
+ eGeometryAppliesWithResize = 0x00001000,
};
layer_state_t()
diff --git a/include/ui/Gralloc1On0Adapter.h b/include/ui/Gralloc1On0Adapter.h
index edcfb65..97c9a89 100644
--- a/include/ui/Gralloc1On0Adapter.h
+++ b/include/ui/Gralloc1On0Adapter.h
@@ -22,6 +22,7 @@
#include <hardware/gralloc1.h>
+#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
@@ -468,8 +469,10 @@
std::shared_ptr<Buffer> getBuffer(buffer_handle_t bufferHandle);
static std::atomic<gralloc1_buffer_descriptor_t> sNextBufferDescriptorId;
+ std::mutex mDescriptorMutex;
std::unordered_map<gralloc1_buffer_descriptor_t,
std::shared_ptr<Descriptor>> mDescriptors;
+ std::mutex mBufferMutex;
std::unordered_map<buffer_handle_t, std::shared_ptr<Buffer>> mBuffers;
};
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index efe3b5e..5a2492e 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/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index f48e58a..b574f9d 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -47,6 +47,12 @@
android_atomic_inc(&counter));
}
+static uint64_t getUniqueId() {
+ static std::atomic<uint32_t> counter{0};
+ static uint64_t id = static_cast<uint64_t>(getpid()) << 32;
+ return id | counter++;
+}
+
BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
mAllocator(allocator),
mMutex(),
@@ -85,7 +91,8 @@
mAutoRefresh(false),
mSharedBufferSlot(INVALID_BUFFER_SLOT),
mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
- HAL_DATASPACE_UNKNOWN)
+ HAL_DATASPACE_UNKNOWN),
+ mUniqueId(getUniqueId())
{
if (allocator == NULL) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 3a0a283..b7b56f0 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -509,11 +509,15 @@
mCore->mIsAllocatingCondition.broadcast();
if (graphicBuffer == NULL) {
+ mCore->mFreeSlots.insert(*outSlot);
+ mCore->clearBufferSlotLocked(*outSlot);
BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
return error;
}
if (mCore->mIsAbandoned) {
+ mCore->mFreeSlots.insert(*outSlot);
+ mCore->clearBufferSlotLocked(*outSlot);
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
@@ -553,44 +557,53 @@
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer: slot %d", slot);
- Mutex::Autolock lock(mCore->mMutex);
- if (mCore->mIsAbandoned) {
- BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
- return NO_INIT;
+ sp<IConsumerListener> listener;
+ {
+ Mutex::Autolock lock(mCore->mMutex);
+
+ if (mCore->mIsAbandoned) {
+ BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
+ return NO_INIT;
+ }
+
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
+ return NO_INIT;
+ }
+
+ if (mCore->mSharedBufferMode || mCore->mSharedBufferSlot == slot) {
+ BQ_LOGE("detachBuffer: cannot detach a buffer in shared buffer mode");
+ return BAD_VALUE;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
+ slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
+ BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
+ return BAD_VALUE;
+ } else if (!mSlots[slot].mRequestBufferCalled) {
+ BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
+ slot);
+ return BAD_VALUE;
+ }
+
+ mSlots[slot].mBufferState.detachProducer();
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeSlots.insert(slot);
+ mCore->clearBufferSlotLocked(slot);
+ mCore->mDequeueCondition.broadcast();
+ VALIDATE_CONSISTENCY();
+ listener = mCore->mConsumerListener;
}
- if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
- return NO_INIT;
+ if (listener != NULL) {
+ listener->onBuffersReleased();
}
- if (mCore->mSharedBufferMode || mCore->mSharedBufferSlot == slot) {
- BQ_LOGE("detachBuffer: cannot detach a buffer in shared buffer mode");
- return BAD_VALUE;
- }
-
- if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
- slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return BAD_VALUE;
- } else if (!mSlots[slot].mBufferState.isDequeued()) {
- BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- return BAD_VALUE;
- } else if (!mSlots[slot].mRequestBufferCalled) {
- BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
- slot);
- return BAD_VALUE;
- }
-
- mSlots[slot].mBufferState.detachProducer();
- mCore->mActiveBuffers.erase(slot);
- mCore->mFreeSlots.insert(slot);
- mCore->clearBufferSlotLocked(slot);
- mCore->mDequeueCondition.broadcast();
- VALIDATE_CONSISTENCY();
-
return NO_ERROR;
}
@@ -887,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());
@@ -1094,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
@@ -1329,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);
@@ -1430,4 +1437,11 @@
disconnect(api);
}
+status_t BufferQueueProducer::getUniqueId(uint64_t* outId) const {
+ BQ_LOGV("getUniqueId");
+
+ *outId = mCore->mUniqueId;
+ return NO_ERROR;
+}
+
} // namespace android
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index ab83317..56656e3 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -50,12 +50,12 @@
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,
GET_LAST_QUEUED_BUFFER,
- GET_FRAME_TIMESTAMPS
+ GET_FRAME_TIMESTAMPS,
+ GET_UNIQUE_ID
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -133,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;
@@ -171,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;
@@ -333,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(
@@ -455,6 +456,25 @@
}
return found;
}
+
+ virtual status_t getUniqueId(uint64_t* outId) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ status_t result = remote()->transact(GET_UNIQUE_ID, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getUniqueId failed to transact: %d", result);
+ }
+ status_t actualResult = NO_ERROR;
+ result = reply.readInt32(&actualResult);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply.readUint64(outId);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return actualResult;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -541,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;
@@ -564,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;
}
@@ -639,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();
@@ -720,6 +738,20 @@
}
return NO_ERROR;
}
+ case GET_UNIQUE_ID: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ uint64_t outId = 0;
+ status_t actualResult = getUniqueId(&outId);
+ status_t result = reply->writeInt32(actualResult);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeUint64(outId);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index decffbf..47cb047 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -41,7 +41,8 @@
CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
DESTROY_SURFACE,
CLEAR_LAYER_FRAME_STATS,
- GET_LAYER_FRAME_STATS
+ GET_LAYER_FRAME_STATS,
+ GET_TRANSFORM_TO_DISPLAY_INVERSE
};
class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
@@ -94,6 +95,35 @@
reply.read(*outStats);
return reply.readInt32();
}
+
+ virtual status_t getTransformToDisplayInverse(const sp<IBinder>& handle,
+ bool* outTransformToDisplayInverse) const {
+ Parcel data, reply;
+ status_t result =
+ data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = data.writeStrongBinder(handle);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = remote()->transact(GET_TRANSFORM_TO_DISPLAY_INVERSE, data, &reply);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ int transformInverse;
+ result = reply.readInt32(&transformInverse);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ *outTransformToDisplayInverse = transformInverse != 0 ? true : false;
+ status_t result2 = reply.readInt32(&result);
+ if (result2 != NO_ERROR) {
+ return result2;
+ }
+ return result;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -145,6 +175,25 @@
reply->writeInt32(result);
return NO_ERROR;
}
+ case GET_TRANSFORM_TO_DISPLAY_INVERSE: {
+ CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
+ sp<IBinder> handle;
+ status_t result = data.readStrongBinder(&handle);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ bool transformInverse = false;
+ result = getTransformToDisplayInverse(handle, &transformInverse);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeInt32(transformInverse ? 1 : 0);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ result = reply->writeInt32(NO_ERROR);
+ return result;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/Sensor.cpp b/libs/gui/Sensor.cpp
index cc865d1..053d153 100644
--- a/libs/gui/Sensor.cpp
+++ b/libs/gui/Sensor.cpp
@@ -408,6 +408,15 @@
return mUuid;
}
+void Sensor::setId(int32_t id) {
+ mUuid.i64[0] = id;
+ mUuid.i64[1] = 0;
+}
+
+int32_t Sensor::getId() const {
+ return int32_t(mUuid.i64[0]);
+}
+
size_t Sensor::getFlattenedSize() const {
size_t fixedSize =
sizeof(mVersion) + sizeof(mHandle) + sizeof(mType) +
@@ -448,7 +457,18 @@
FlattenableUtils::write(buffer, size, mRequiredAppOp);
FlattenableUtils::write(buffer, size, mMaxDelay);
FlattenableUtils::write(buffer, size, mFlags);
- FlattenableUtils::write(buffer, size, mUuid);
+ if (mUuid.i64[1] != 0) {
+ // We should never hit this case with our current API, but we
+ // could via a careless API change. If that happens,
+ // this code will keep us from leaking our UUID (while probably
+ // breaking dynamic sensors). See b/29547335.
+ ALOGW("Sensor with UUID being flattened; sending 0. Expect "
+ "bad dynamic sensor behavior");
+ uuid_t tmpUuid; // default constructor makes this 0.
+ FlattenableUtils::write(buffer, size, tmpUuid);
+ } else {
+ FlattenableUtils::write(buffer, size, mUuid);
+ }
return NO_ERROR;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 4739ca4..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 {
@@ -292,8 +294,10 @@
int buf = -1;
sp<Fence> fence;
+ nsecs_t now = systemTime();
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence,
reqWidth, reqHeight, reqFormat, reqUsage);
+ mLastDequeueDuration = systemTime() - now;
if (result < 0) {
ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer"
@@ -496,7 +500,9 @@
input.setSurfaceDamage(flippedRegion);
}
+ nsecs_t now = systemTime();
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
+ mLastQueueDuration = systemTime() - now;
if (err != OK) {
ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
}
@@ -504,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) {
@@ -575,6 +581,20 @@
}
return err;
}
+ case NATIVE_WINDOW_LAST_DEQUEUE_DURATION: {
+ int64_t durationUs = mLastDequeueDuration / 1000;
+ *value = durationUs > std::numeric_limits<int>::max() ?
+ std::numeric_limits<int>::max() :
+ static_cast<int>(durationUs);
+ return NO_ERROR;
+ }
+ case NATIVE_WINDOW_LAST_QUEUE_DURATION: {
+ int64_t durationUs = mLastQueueDuration / 1000;
+ *value = durationUs > std::numeric_limits<int>::max() ?
+ std::numeric_limits<int>::max() :
+ static_cast<int>(durationUs);
+ return NO_ERROR;
+ }
}
}
return mGraphicBufferProducer->query(what, value);
@@ -802,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) {
@@ -1322,13 +1342,17 @@
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;
}
+status_t Surface::getUniqueId(uint64_t* outId) const {
+ Mutex::Autolock lock(mMutex);
+ return mGraphicBufferProducer->getUniqueId(outId);
+}
+
namespace view {
status_t Surface::writeToParcel(Parcel* parcel) const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2189047..059223d 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -165,7 +165,7 @@
uint64_t frameNumber);
status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, int32_t overrideScalingMode);
- status_t setPositionAppliesWithResize(const sp<SurfaceComposerClient>& client,
+ status_t setGeometryAppliesWithResize(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id);
void setDisplaySurface(const sp<IBinder>& token,
@@ -445,7 +445,7 @@
return NO_ERROR;
}
-status_t Composer::setPositionAppliesWithResize(
+status_t Composer::setGeometryAppliesWithResize(
const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id) {
Mutex::Autolock lock(mLock);
@@ -453,7 +453,7 @@
if (!s) {
return BAD_INDEX;
}
- s->what |= layer_state_t::ePositionAppliesWithResize;
+ s->what |= layer_state_t::eGeometryAppliesWithResize;
return NO_ERROR;
}
@@ -612,6 +612,14 @@
return mClient->getLayerFrameStats(token, outStats);
}
+status_t SurfaceComposerClient::getTransformToDisplayInverse(const sp<IBinder>& token,
+ bool* outTransformToDisplayInverse) const {
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ return mClient->getTransformToDisplayInverse(token, outTransformToDisplayInverse);
+}
+
inline Composer& SurfaceComposerClient::getComposer() {
return mComposer;
}
@@ -699,9 +707,9 @@
this, id, overrideScalingMode);
}
-status_t SurfaceComposerClient::setPositionAppliesWithResize(
+status_t SurfaceComposerClient::setGeometryAppliesWithResize(
const sp<IBinder>& id) {
- return getComposer().setPositionAppliesWithResize(this, id);
+ return getComposer().setGeometryAppliesWithResize(this, id);
}
// ----------------------------------------------------------------------------
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 4671e50..33c1d90 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -112,10 +112,10 @@
if (err < 0) return err;
return mClient->setPosition(mHandle, x, y);
}
-status_t SurfaceControl::setPositionAppliesWithResize() {
+status_t SurfaceControl::setGeometryAppliesWithResize() {
status_t err = validate();
if (err < 0) return err;
- return mClient->setPositionAppliesWithResize(mHandle);
+ return mClient->setGeometryAppliesWithResize(mHandle);
}
status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
status_t err = validate();
@@ -190,6 +190,13 @@
return client->getLayerFrameStats(mHandle, outStats);
}
+status_t SurfaceControl::getTransformToDisplayInverse(bool* outTransformToDisplayInverse) const {
+ status_t err = validate();
+ if (err < 0) return err;
+ const sp<SurfaceComposerClient>& client(mClient);
+ return client->getTransformToDisplayInverse(mHandle, outTransformToDisplayInverse);
+}
+
status_t SurfaceControl::validate() const
{
if (mHandle==0 || mClient==0) {
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/libs/ui/Gralloc1On0Adapter.cpp b/libs/ui/Gralloc1On0Adapter.cpp
index 6e69df1..d5b88de 100644
--- a/libs/ui/Gralloc1On0Adapter.cpp
+++ b/libs/ui/Gralloc1On0Adapter.cpp
@@ -193,6 +193,7 @@
gralloc1_buffer_descriptor_t* outDescriptor)
{
auto descriptorId = sNextBufferDescriptorId++;
+ std::lock_guard<std::mutex> lock(mDescriptorMutex);
mDescriptors.emplace(descriptorId,
std::make_shared<Descriptor>(this, descriptorId));
@@ -207,6 +208,7 @@
{
ALOGV("Destroying descriptor %" PRIu64, descriptor);
+ std::lock_guard<std::mutex> lock(mDescriptorMutex);
if (mDescriptors.count(descriptor) == 0) {
return GRALLOC1_ERROR_BAD_DESCRIPTOR;
}
@@ -255,6 +257,8 @@
*outBufferHandle = handle;
auto buffer = std::make_shared<Buffer>(handle, store, *descriptor, stride,
true);
+
+ std::lock_guard<std::mutex> lock(mBufferMutex);
mBuffers.emplace(handle, std::move(buffer));
return GRALLOC1_ERROR_NONE;
@@ -309,6 +313,8 @@
ALOGE("gralloc0 unregister failed: %d", result);
}
}
+
+ std::lock_guard<std::mutex> lock(mBufferMutex);
mBuffers.erase(handle);
return GRALLOC1_ERROR_NONE;
}
@@ -320,6 +326,7 @@
graphicBuffer->getNativeBuffer()->handle, graphicBuffer->getId());
buffer_handle_t handle = graphicBuffer->getNativeBuffer()->handle;
+ std::lock_guard<std::mutex> lock(mBufferMutex);
if (mBuffers.count(handle) != 0) {
mBuffers[handle]->retain();
return GRALLOC1_ERROR_NONE;
@@ -446,6 +453,7 @@
std::shared_ptr<Gralloc1On0Adapter::Descriptor>
Gralloc1On0Adapter::getDescriptor(gralloc1_buffer_descriptor_t descriptorId)
{
+ std::lock_guard<std::mutex> lock(mDescriptorMutex);
if (mDescriptors.count(descriptorId) == 0) {
return nullptr;
}
@@ -456,6 +464,7 @@
std::shared_ptr<Gralloc1On0Adapter::Buffer> Gralloc1On0Adapter::getBuffer(
buffer_handle_t bufferHandle)
{
+ std::lock_guard<std::mutex> lock(mBufferMutex);
if (mBuffers.count(bufferHandle) == 0) {
return nullptr;
}
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/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index bef5f02..6fab4d9 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -545,7 +545,7 @@
#define EGL_SYNC_NATIVE_FENCE_ANDROID 0x3144
#define EGL_SYNC_NATIVE_FENCE_FD_ANDROID 0x3145
#define EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID 0x3146
-#define EGL_NO_NATIVE_FENCE_FD_ANDROID -1
+#define EGL_NO_NATIVE_FENCE_FD_ANDROID (-1)
#ifdef EGL_EGLEXT_PROTOTYPES
EGLAPI EGLint EGLAPIENTRY eglDupNativeFenceFDANDROID( EGLDisplay dpy, EGLSyncKHR);
#endif /* EGL_EGLEXT_PROTOTYPES */
@@ -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/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 e793852..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,6 +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
@@ -207,6 +212,12 @@
(__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
{ "eglCreateStreamFromFileDescriptorKHR",
(__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+ // EGL_ANDROID_get_frame_timestamps
+ { "eglGetFrameTimestampsANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglQueryTimestampSupportedANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID },
};
/*
@@ -1196,7 +1207,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 +1216,13 @@
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(
dp->disp.dpy, s->surface, attribute, value);
@@ -1935,3 +1953,105 @@
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) {
+#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS
+ 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;
+#endif
+ 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/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index a2d689b..b9be675 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -134,10 +134,14 @@
{ AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN },
{ AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT },
{ AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP },
- { AKEYCODE_FP_NAV_DOWN, AKEYCODE_FP_NAV_RIGHT, AKEYCODE_FP_NAV_UP, AKEYCODE_FP_NAV_LEFT },
- { AKEYCODE_FP_NAV_RIGHT, AKEYCODE_FP_NAV_UP, AKEYCODE_FP_NAV_LEFT, AKEYCODE_FP_NAV_DOWN },
- { AKEYCODE_FP_NAV_UP, AKEYCODE_FP_NAV_LEFT, AKEYCODE_FP_NAV_DOWN, AKEYCODE_FP_NAV_RIGHT },
- { AKEYCODE_FP_NAV_LEFT, AKEYCODE_FP_NAV_DOWN, AKEYCODE_FP_NAV_RIGHT, AKEYCODE_FP_NAV_UP },
+ { AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT,
+ AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT },
+ { AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP,
+ AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN },
+ { AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT,
+ AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT },
+ { AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN,
+ AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP },
};
static const size_t keyCodeRotationMapSize =
sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index 5a36961..7b10319 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -34,7 +34,8 @@
liblog \
libbinder \
libui \
- libgui
+ libgui \
+ libcrypto
LOCAL_MODULE:= libsensorservice
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index f6d3d94..e0101c1 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -178,14 +178,7 @@
if (s.hasAdditionalInfo()) {
result.appendFormat("has-additional-info, ");
}
- result.append("| ");
- if (s.isDynamicSensor()) {
- result.append("uuid: ");
- for (uint8_t i : s.getUuid().b) {
- result.appendFormat("%02x", i);
- }
- }
result.append("\n");
return true;
});
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 6caa85b..0c4dc26 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -26,6 +26,10 @@
#include <hardware/sensors.h>
#include <hardware_legacy/power.h>
+#include <openssl/digest.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+
#include "BatteryService.h"
#include "CorrectedGyroSensor.h"
#include "GravitySensor.h"
@@ -44,8 +48,10 @@
#include <inttypes.h>
#include <math.h>
#include <stdint.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
namespace android {
// ---------------------------------------------------------------------------
@@ -60,6 +66,12 @@
*/
const char* SensorService::WAKE_LOCK_NAME = "SensorService_wakelock";
+uint8_t SensorService::sHmacGlobalKey[128] = {};
+bool SensorService::sHmacGlobalKeyIsValid = false;
+
+#define SENSOR_SERVICE_DIR "/data/system/sensor_service"
+#define SENSOR_SERVICE_HMAC_KEY_FILE SENSOR_SERVICE_DIR "/hmac_key"
+
// Permissions.
static const String16 sDump("android.permission.DUMP");
@@ -68,10 +80,49 @@
mWakeLockAcquired(false) {
}
+bool SensorService::initializeHmacKey() {
+ int fd = open(SENSOR_SERVICE_HMAC_KEY_FILE, O_RDONLY|O_CLOEXEC);
+ if (fd != -1) {
+ int result = read(fd, sHmacGlobalKey, sizeof(sHmacGlobalKey));
+ close(fd);
+ if (result == sizeof(sHmacGlobalKey)) {
+ return true;
+ }
+ ALOGW("Unable to read HMAC key; generating new one.");
+ }
+
+ if (RAND_bytes(sHmacGlobalKey, sizeof(sHmacGlobalKey)) == -1) {
+ ALOGW("Can't generate HMAC key; dynamic sensor getId() will be wrong.");
+ return false;
+ }
+
+ // We need to make sure this is only readable to us.
+ bool wroteKey = false;
+ mkdir(SENSOR_SERVICE_DIR, S_IRWXU);
+ fd = open(SENSOR_SERVICE_HMAC_KEY_FILE, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC,
+ S_IRUSR|S_IWUSR);
+ if (fd != -1) {
+ int result = write(fd, sHmacGlobalKey, sizeof(sHmacGlobalKey));
+ close(fd);
+ wroteKey = (result == sizeof(sHmacGlobalKey));
+ }
+ if (wroteKey) {
+ ALOGI("Generated new HMAC key.");
+ } else {
+ ALOGW("Unable to write HMAC key; dynamic sensor getId() will change "
+ "after reboot.");
+ }
+ // Even if we failed to write the key we return true, because we did
+ // initialize the HMAC key.
+ return true;
+}
+
void SensorService::onFirstRef() {
ALOGD("nuSensorService starting...");
SensorDevice& dev(SensorDevice::getInstance());
+ sHmacGlobalKeyIsValid = initializeHmacKey();
+
if (dev.initCheck() == NO_ERROR) {
sensor_t const* list;
ssize_t count = dev.getSensorList(&list);
@@ -384,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;
@@ -719,6 +770,85 @@
return sensor != nullptr && sensor->getSensor().isWakeUpSensor();
}
+int32_t SensorService::getIdFromUuid(const Sensor::uuid_t &uuid) const {
+ if ((uuid.i64[0] == 0) && (uuid.i64[1] == 0)) {
+ // UUID is not supported for this device.
+ return 0;
+ }
+ if ((uuid.i64[0] == INT64_C(~0)) && (uuid.i64[1] == INT64_C(~0))) {
+ // This sensor can be uniquely identified in the system by
+ // the combination of its type and name.
+ return -1;
+ }
+
+ // We have a dynamic sensor.
+
+ if (!sHmacGlobalKeyIsValid) {
+ // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+ ALOGW("HMAC key failure; dynamic sensor getId() will be wrong.");
+ return 0;
+ }
+
+ // We want each app author/publisher to get a different ID, so that the
+ // same dynamic sensor cannot be tracked across apps by multiple
+ // authors/publishers. So we use both our UUID and our User ID.
+ // Note potential confusion:
+ // UUID => Universally Unique Identifier.
+ // UID => User Identifier.
+ // We refrain from using "uid" except as needed by API to try to
+ // keep this distinction clear.
+
+ auto appUserId = IPCThreadState::self()->getCallingUid();
+ uint8_t uuidAndApp[sizeof(uuid) + sizeof(appUserId)];
+ memcpy(uuidAndApp, &uuid, sizeof(uuid));
+ memcpy(uuidAndApp + sizeof(uuid), &appUserId, sizeof(appUserId));
+
+ // Now we use our key on our UUID/app combo to get the hash.
+ uint8_t hash[EVP_MAX_MD_SIZE];
+ unsigned int hashLen;
+ if (HMAC(EVP_sha256(),
+ sHmacGlobalKey, sizeof(sHmacGlobalKey),
+ uuidAndApp, sizeof(uuidAndApp),
+ hash, &hashLen) == nullptr) {
+ // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+ ALOGW("HMAC failure; dynamic sensor getId() will be wrong.");
+ return 0;
+ }
+
+ int32_t id = 0;
+ if (hashLen < sizeof(id)) {
+ // We never expect this case, but out of paranoia, we handle it.
+ // Our 'id' length is already quite small, we don't want the
+ // effective length of it to be even smaller.
+ // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+ ALOGW("HMAC insufficient; dynamic sensor getId() will be wrong.");
+ return 0;
+ }
+
+ // This is almost certainly less than all of 'hash', but it's as secure
+ // as we can be with our current 'id' length.
+ memcpy(&id, hash, sizeof(id));
+
+ // Note at the beginning of the function that we return the values of
+ // 0 and -1 to represent special cases. As a result, we can't return
+ // those as dynamic sensor IDs. If we happened to hash to one of those
+ // values, we change 'id' so we report as a dynamic sensor, and not as
+ // one of those special cases.
+ if (id == -1) {
+ id = -2;
+ } else if (id == 0) {
+ id = 1;
+ }
+ return id;
+}
+
+void SensorService::makeUuidsIntoIdsForSensorList(Vector<Sensor> &sensorList) const {
+ for (auto &sensor : sensorList) {
+ int32_t id = getIdFromUuid(sensor.getUuid());
+ sensor.setId(id);
+ }
+}
+
Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) {
char value[PROPERTY_VALUE_MAX];
property_get("debug.sensors", value, "0");
@@ -736,6 +866,7 @@
sensor.getRequiredAppOp());
}
}
+ makeUuidsIntoIdsForSensorList(accessibleSensorList);
return accessibleSensorList;
}
@@ -755,6 +886,7 @@
}
return true;
});
+ makeUuidsIntoIdsForSensorList(accessibleSensorList);
return accessibleSensorList;
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index bcb666b..5f743fc 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 {
// ---------------------------------------------------------------------------
@@ -208,6 +208,17 @@
status_t resetToNormalMode();
status_t resetToNormalModeLocked();
+ // Transforms the UUIDs for all the sensors into proper IDs.
+ void makeUuidsIntoIdsForSensorList(Vector<Sensor> &sensorList) const;
+ // Gets the appropriate ID from the given UUID.
+ int32_t getIdFromUuid(const Sensor::uuid_t &uuid) const;
+ // Either read from storage or create a new one.
+ static bool initializeHmacKey();
+
+
+ static uint8_t sHmacGlobalKey[128];
+ static bool sHmacGlobalKeyIsValid;
+
SensorList mSensors;
status_t mInitCheck;
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index d654b17..2a8fa4a 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -45,8 +45,7 @@
LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-USE_HWC2 := false
-ifeq ($(USE_HWC2),true)
+ifeq ($(TARGET_USES_HWC2),true)
LOCAL_CFLAGS += -DUSE_HWC2
LOCAL_SRC_FILES += \
SurfaceFlinger.cpp \
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 2a025b8..415bdca 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -173,5 +173,15 @@
return NO_ERROR;
}
+status_t Client::getTransformToDisplayInverse(const sp<IBinder>& handle,
+ bool* outTransformToDisplayInverse) const {
+ sp<Layer> layer = getLayerUser(handle);
+ if (layer == NULL) {
+ return NAME_NOT_FOUND;
+ }
+ *outTransformToDisplayInverse = layer->getTransformToDisplayInverse();
+ return NO_ERROR;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index b6d7381..12db505 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -63,6 +63,8 @@
virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const;
+ virtual status_t getTransformToDisplayInverse(
+ const sp<IBinder>& handle, bool* outTransformToDisplayInverse) const;
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
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/HWC2On1Adapter.cpp b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
index 50fc87c..0528d7b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.cpp
@@ -2333,7 +2333,7 @@
int supportedTypes = 0;
auto result = mHwc1Device->query(mHwc1Device,
HWC_DISPLAY_TYPES_SUPPORTED, &supportedTypes);
- if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL) != 0)) {
+ if ((result == 0) && ((supportedTypes & HWC_DISPLAY_VIRTUAL_BIT) != 0)) {
ALOGI("Found support for HWC virtual displays");
mHwc1SupportsVirtualDisplays = true;
}
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/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index bc8dfbb..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;
@@ -612,11 +611,17 @@
return INVALID_OPERATION;
}
+status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const {
+ ALOGE("getUniqueId not supported on VirtualDisplaySurface");
+ return INVALID_OPERATION;
+}
+
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 29563b6..bf9b39c 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -122,12 +122,12 @@
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;
virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) override;
+ virtual status_t getUniqueId(uint64_t* outId) const override;
//
// Utility methods
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/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1554d70..6024fa9 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -724,11 +724,6 @@
// setup a solid color layer yet
ALOGV("[%s] Requesting Client composition", mName.string());
setCompositionType(hwcId, HWC2::Composition::Client);
- error = hwcLayer->setBuffer(nullptr, Fence::NO_FENCE);
- if (error != HWC2::Error::None) {
- ALOGE("[%s] Failed to set null buffer: %s (%d)", mName.string(),
- to_string(error).c_str(), static_cast<int32_t>(error));
- }
return;
}
@@ -1103,6 +1098,25 @@
}
}
+bool Layer::headFenceHasSignaled() const {
+#ifdef USE_HWC2
+ Mutex::Autolock lock(mQueueItemLock);
+ if (mQueueItems.empty()) {
+ return true;
+ }
+ if (mQueueItems[0].mIsDroppable) {
+ // Even though this buffer's fence may not have signaled yet, it could
+ // be replaced by another buffer before it has a chance to, which means
+ // that it's possible to get into a situation where a buffer is never
+ // able to be latched. To avoid this, grab this buffer anyway.
+ return true;
+ }
+ return mQueueItems[0].mFence->getSignalTime() != INT64_MAX;
+#else
+ return true;
+#endif
+}
+
bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
if (point->getFrameNumber() <= mCurrentFrameNumber) {
// Don't bother with a SyncPoint, since we've already latched the
@@ -1360,9 +1374,10 @@
void Layer::notifyAvailableFrames() {
auto headFrameNumber = getHeadFrameNumber();
+ bool headFenceSignaled = headFenceHasSignaled();
Mutex::Autolock lock(mLocalSyncPointMutex);
for (auto& point : mLocalSyncPoints) {
- if (headFrameNumber >= point->getFrameNumber()) {
+ if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) {
point->setFrameAvailable();
}
}
@@ -1572,11 +1587,15 @@
setTransactionFlags(eTransactionNeeded);
return true;
}
-bool Layer::setCrop(const Rect& crop) {
+
+bool Layer::setCrop(const Rect& crop, bool immediate) {
if (mCurrentState.crop == crop)
return false;
mCurrentState.sequence++;
- mCurrentState.crop = crop;
+ mCurrentState.requestedCrop = crop;
+ if (immediate) {
+ mCurrentState.crop = crop;
+ }
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -1755,6 +1774,13 @@
return outDirtyRegion;
}
+ // If the head buffer's acquire fence hasn't signaled yet, return and
+ // try again later
+ if (!headFenceHasSignaled()) {
+ mFlinger->signalLayerUpdate();
+ return outDirtyRegion;
+ }
+
// Capture the old state of the layer for comparisons later
const State& s(getDrawingState());
const bool oldOpacity = isOpaque(s);
@@ -1767,16 +1793,19 @@
bool stickyTransformSet;
const char* name;
int32_t overrideScalingMode;
+ bool& freezePositionUpdates;
Reject(Layer::State& front, Layer::State& current,
bool& recomputeVisibleRegions, bool stickySet,
const char* name,
- int32_t overrideScalingMode)
+ int32_t overrideScalingMode,
+ bool& freezePositionUpdates)
: front(front), current(current),
recomputeVisibleRegions(recomputeVisibleRegions),
stickyTransformSet(stickySet),
name(name),
- overrideScalingMode(overrideScalingMode) {
+ overrideScalingMode(overrideScalingMode),
+ freezePositionUpdates(freezePositionUpdates) {
}
virtual bool reject(const sp<GraphicBuffer>& buf,
@@ -1868,13 +1897,20 @@
recomputeVisibleRegions = true;
}
+ if (front.crop != front.requestedCrop) {
+ front.crop = front.requestedCrop;
+ current.crop = front.requestedCrop;
+ recomputeVisibleRegions = true;
+ }
+ freezePositionUpdates = false;
+
return false;
}
};
Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
getProducerStickyTransform() != 0, mName.string(),
- mOverrideScalingMode);
+ mOverrideScalingMode, mFreezePositionUpdates);
// Check all of our local sync points to ensure that all transactions
@@ -2017,7 +2053,6 @@
if (bufWidth != uint32_t(oldActiveBuffer->width) ||
bufHeight != uint32_t(oldActiveBuffer->height)) {
recomputeVisibleRegions = true;
- mFreezePositionUpdates = false;
}
}
@@ -2195,6 +2230,10 @@
return history;
}
+bool Layer::getTransformToDisplayInverse() const {
+ return mSurfaceFlingerConsumer->getTransformToDisplayInverse();
+}
+
// ---------------------------------------------------------------------------
Layer::LayerCleaner::LayerCleaner(const sp<SurfaceFlinger>& flinger,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4257c37..78a8427 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -96,7 +96,9 @@
Transform transform;
inline bool operator ==(const Geometry& rhs) const {
- return (w == rhs.w && h == rhs.h);
+ return (w == rhs.w && h == rhs.h) &&
+ (transform.tx() == rhs.transform.tx()) &&
+ (transform.ty() == rhs.transform.ty());
}
inline bool operator !=(const Geometry& rhs) const {
return !operator ==(rhs);
@@ -120,6 +122,8 @@
bool modified;
Rect crop;
+ Rect requestedCrop;
+
Rect finalCrop;
// If set, defers this state update until the Layer identified by handle
@@ -156,7 +160,7 @@
bool setMatrix(const layer_state_t::matrix22_t& matrix);
bool setTransparentRegionHint(const Region& transparent);
bool setFlags(uint8_t flags, uint8_t mask);
- bool setCrop(const Rect& crop);
+ bool setCrop(const Rect& crop, bool immediate);
bool setFinalCrop(const Rect& crop);
bool setLayerStack(uint32_t layerStack);
void deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
@@ -414,6 +418,8 @@
return mFlinger->getFrameTimestamps(*this, frameNumber, outTimestamps);
}
+ bool getTransformToDisplayInverse() const;
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -504,6 +510,7 @@
std::list<std::shared_ptr<SyncPoint>> mRemoteSyncPoints;
uint64_t getHeadFrameNumber() const;
+ bool headFenceHasSignaled() const;
// Returns false if the relevant frame has already been latched
bool addSyncPoint(const std::shared_ptr<SyncPoint>& point);
diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp
index 34dc24b..974c7a3 100644
--- a/services/surfaceflinger/MessageQueue.cpp
+++ b/services/surfaceflinger/MessageQueue.cpp
@@ -134,31 +134,12 @@
}
-/* when INVALIDATE_ON_VSYNC is set SF only processes
- * buffer updates on VSYNC and performs a refresh immediately
- * after.
- *
- * when INVALIDATE_ON_VSYNC is set to false, SF will instead
- * perform the buffer updates immediately, but the refresh only
- * at the next VSYNC.
- * THIS MODE IS BUGGY ON GALAXY NEXUS AND WILL CAUSE HANGS
- */
-#define INVALIDATE_ON_VSYNC 1
-
void MessageQueue::invalidate() {
-#if INVALIDATE_ON_VSYNC
mEvents->requestNextVsync();
-#else
- mHandler->dispatchInvalidate();
-#endif
}
void MessageQueue::refresh() {
-#if INVALIDATE_ON_VSYNC
mHandler->dispatchRefresh();
-#else
- mEvents->requestNextVsync();
-#endif
}
int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
@@ -172,11 +153,7 @@
while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
for (int i=0 ; i<n ; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-#if INVALIDATE_ON_VSYNC
mHandler->dispatchInvalidate();
-#else
- mHandler->dispatchRefresh();
-#endif
break;
}
}
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index faab62c..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);
}
@@ -149,6 +145,10 @@
outTransformMatrix);
}
+status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
+ return mProducer->getUniqueId(outId);
+}
+
IBinder* MonitoredProducer::onAsBinder() {
return IInterface::asBinder(mProducer).get();
}
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index ce756dc..f64fe51 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -57,13 +57,13 @@
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;
virtual IBinder* onAsBinder();
virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
virtual status_t setAutoRefresh(bool autoRefresh) override;
+ virtual status_t getUniqueId(uint64_t* outId) const override;
private:
sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7ce7777..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(),
@@ -167,9 +179,6 @@
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
mGpuToCpuSupported = !atoi(value);
- property_get("debug.sf.drop_missed_frames", value, "0");
- mDropMissedFrames = atoi(value);
-
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
@@ -465,6 +474,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);
@@ -605,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
@@ -626,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;
@@ -904,6 +933,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;
@@ -940,36 +978,22 @@
ATRACE_CALL();
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
- static nsecs_t previousExpectedPresent = 0;
- nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
- static bool previousFrameMissed = false;
- bool frameMissed = (expectedPresent == previousExpectedPresent);
- if (frameMissed != previousFrameMissed) {
- ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
- }
- previousFrameMissed = frameMissed;
- if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) {
- // Latch buffers, but don't send anything to HWC, then signal another
- // wakeup for the next vsync
- preComposition();
- repaintEverything();
- } else {
- preComposition();
- rebuildLayerStacks();
- setUpHWComposer();
- doDebugFlashRegions();
- doComposition();
- postComposition(refreshStartTime);
- }
+ preComposition();
+ rebuildLayerStacks();
+ setUpHWComposer();
+ doDebugFlashRegions();
+ doComposition();
+ postComposition(refreshStartTime);
+
+ mPreviousPresentFence = mHwc->getRetireFence(HWC_DISPLAY_PRIMARY);
+ mHadClientComposition = mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY);
// Release any buffers which were replaced this frame
for (auto& layer : mLayersWithQueuedFrames) {
layer->releasePendingBuffer();
}
mLayersWithQueuedFrames.clear();
-
- previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0);
}
void SurfaceFlinger::doDebugFlashRegions()
@@ -1005,7 +1029,12 @@
}
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
- status_t result = mDisplays[displayId]->prepareFrame(*mHwc);
+ auto& displayDevice = mDisplays[displayId];
+ if (!displayDevice->isDisplayOn()) {
+ continue;
+ }
+
+ status_t result = displayDevice->prepareFrame(*mHwc);
ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:"
" %d (%s)", displayId, result, strerror(-result));
}
@@ -1207,8 +1236,7 @@
}
layer->setGeometry(displayDevice);
- if (mDebugDisableHWC || mDebugRegion || mDaltonize ||
- mHasColorMatrix) {
+ if (mDebugDisableHWC || mDebugRegion) {
layer->forceClientComposition(hwcId);
}
}
@@ -1216,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];
@@ -1223,13 +1254,25 @@
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) {
- status_t result = mDisplays[displayId]->prepareFrame(*mHwc);
+ auto& displayDevice = mDisplays[displayId];
+ if (!displayDevice->isDisplayOn()) {
+ continue;
+ }
+
+ status_t result = displayDevice->prepareFrame(*mHwc);
ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:"
" %d (%s)", displayId, result, strerror(-result));
}
@@ -1267,6 +1310,9 @@
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
+ if (!displayDevice->isDisplayOn()) {
+ continue;
+ }
const auto hwcId = displayDevice->getHwcDisplayId();
if (hwcId >= 0) {
mHwc->commit(hwcId);
@@ -1901,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);
@@ -1927,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");
@@ -2044,6 +2088,10 @@
}
}
+ if (applyColorMatrix) {
+ getRenderEngine().setupColorTransform(oldColorMatrix);
+ }
+
// disable scissor at the end of the frame
mRenderEngine->disableScissor();
return true;
@@ -2247,10 +2295,10 @@
sp<Layer> layer(client->getLayerUser(s.surface));
if (layer != 0) {
const uint32_t what = s.what;
- bool positionAppliesWithResize =
- what & layer_state_t::ePositionAppliesWithResize;
+ bool geometryAppliesWithResize =
+ what & layer_state_t::eGeometryAppliesWithResize;
if (what & layer_state_t::ePositionChanged) {
- if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) {
+ if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) {
flags |= eTraversalNeeded;
}
}
@@ -2287,7 +2335,7 @@
flags |= eTraversalNeeded;
}
if (what & layer_state_t::eCropChanged) {
- if (layer->setCrop(s.crop))
+ if (layer->setCrop(s.crop, !geometryAppliesWithResize))
flags |= eTraversalNeeded;
}
if (what & layer_state_t::eFinalCropChanged) {
@@ -2919,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);
@@ -3075,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;
@@ -3092,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 28666e2..f063aaf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -468,7 +468,6 @@
RenderEngine* mRenderEngine;
nsecs_t mBootTime;
bool mGpuToCpuSupported;
- bool mDropMissedFrames;
sp<EventThread> mEventThread;
sp<EventThread> mSFEventThread;
sp<EventControlThread> mEventControlThread;
@@ -488,6 +487,8 @@
bool mAnimCompositionPending;
#ifdef USE_HWC2
std::vector<sp<Layer>> mLayersWithQueuedFrames;
+ sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
+ bool mHadClientComposition = false;
#endif
// this may only be written from the main thread with mStateLock held
@@ -526,8 +527,11 @@
*/
Daltonizer mDaltonizer;
+#ifndef USE_HWC2
bool mDaltonize;
+#endif
+ mat4 mPreviousColorMatrix;
mat4 mColorMatrix;
bool mHasColorMatrix;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index ba0a527..e0e4c61 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -129,6 +129,7 @@
}
bool SurfaceFlingerConsumer::getTransformToDisplayInverse() const {
+ Mutex::Autolock lock(mMutex);
return mTransformToDisplayInverse;
}
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
index 3762659..4271039 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.h
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.h
@@ -66,8 +66,9 @@
// See GLConsumer::bindTextureImageLocked().
status_t bindTextureImage();
- // must be called from SF main thread
bool getTransformToDisplayInverse() const;
+
+ // must be called from SF main thread
const Region& getSurfaceDamage() const;
// Sets the contents changed listener. This should be used instead of
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index b49f8af..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
@@ -166,9 +179,6 @@
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
mGpuToCpuSupported = !atoi(value);
- property_get("debug.sf.drop_missed_frames", value, "0");
- mDropMissedFrames = atoi(value);
-
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
@@ -462,6 +472,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,
@@ -944,30 +966,13 @@
ATRACE_CALL();
nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
- static nsecs_t previousExpectedPresent = 0;
- nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
- static bool previousFrameMissed = false;
- bool frameMissed = (expectedPresent == previousExpectedPresent);
- if (frameMissed != previousFrameMissed) {
- ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
- }
- previousFrameMissed = frameMissed;
- if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) {
- // Latch buffers, but don't send anything to HWC, then signal another
- // wakeup for the next vsync
- preComposition();
- repaintEverything();
- } else {
- preComposition();
- rebuildLayerStacks();
- setUpHWComposer();
- doDebugFlashRegions();
- doComposition();
- postComposition(refreshStartTime);
- }
-
- previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0);
+ preComposition();
+ rebuildLayerStacks();
+ setUpHWComposer();
+ doDebugFlashRegions();
+ doComposition();
+ postComposition(refreshStartTime);
}
void SurfaceFlinger::doDebugFlashRegions()
@@ -2263,10 +2268,10 @@
sp<Layer> layer(client->getLayerUser(s.surface));
if (layer != 0) {
const uint32_t what = s.what;
- bool positionAppliesWithResize =
- what & layer_state_t::ePositionAppliesWithResize;
+ bool geometryAppliesWithResize =
+ what & layer_state_t::eGeometryAppliesWithResize;
if (what & layer_state_t::ePositionChanged) {
- if (layer->setPosition(s.x, s.y, !positionAppliesWithResize)) {
+ if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) {
flags |= eTraversalNeeded;
}
}
@@ -2303,7 +2308,7 @@
flags |= eTraversalNeeded;
}
if (what & layer_state_t::eCropChanged) {
- if (layer->setCrop(s.crop))
+ if (layer->setCrop(s.crop, !geometryAppliesWithResize))
flags |= eTraversalNeeded;
}
if (what & layer_state_t::eFinalCropChanged) {
@@ -3088,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();
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();
diff --git a/vulkan/doc/DevelopersGuide.pdf b/vulkan/doc/DevelopersGuide.pdf
deleted file mode 100644
index cf009c5..0000000
--- a/vulkan/doc/DevelopersGuide.pdf
+++ /dev/null
Binary files differ
diff --git a/vulkan/doc/implementors_guide/implementors_guide.adoc b/vulkan/doc/implementors_guide/implementors_guide.adoc
index ae46f43..7ace777 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.adoc
+++ b/vulkan/doc/implementors_guide/implementors_guide.adoc
@@ -6,8 +6,6 @@
This document is intended for GPU IHVs writing Vulkan drivers for Android, and OEMs integrating them for specific devices. It describes how a Vulkan driver interacts with the system, how GPU-specific tools should be installed, and Android-specific requirements.
-This is still a fairly rough draft; details will be filled in over time.
-
== Architecture ==
The primary interface between Vulkan applications and a device's Vulkan driver is the loader, which is part of AOSP and installed at +/system/lib[64]/libvulkan.so+. The loader provides the core Vulkan API entry points, as well as entry points of a few extensions that are required on Android and always present. In particular, the window system integration (WSI) extensions are exported by the loader and primarily implemented in it rather than the driver. The loader also supports enumerating and loading layers which can expose additional extensions and/or intercept core API calls on their way to the driver.
@@ -16,16 +14,16 @@
=== Driver Enumeration and Loading ===
-Android expects the GPUs available to the system to be known when the system image is built, so its driver enumeration process isn't as elaborate as other platforms. The loader will use the existing HAL mechanism (see https://android.googlesource.com/platform/hardware/libhardware/+/lollipop-mr1-release/include/hardware/hardware.h[hardware.h]) for discovering and loading the driver. As of this writing, the preferred paths for 32-bit and 64-bit Vulkan drivers are:
+Android expects the GPUs available to the system to be known when the system image is built, so its driver enumeration process isn't as elaborate as on other platforms. The loader will use the existing HAL mechanism for discovering and loading the driver. As of this writing, the preferred paths for 32-bit and 64-bit Vulkan drivers are:
/vendor/lib/hw/vulkan.<ro.product.platform>.so
/vendor/lib64/hw/vulkan.<ro.product.platform>.so
-where +<ro.product.platform>+ is replaced by the value of the system property of that name. See https://android.googlesource.com/platform/hardware/libhardware/+/lollipop-mr1-release/hardware.c[libhardware/hardware.c] for details and supported alternative locations.
+where +<ro.product.platform>+ is replaced by the value of the system property of that name. See https://android.googlesource.com/platform/hardware/libhardware/+/master/hardware.c[libhardware/hardware.c] for details and supported alternative locations.
The Vulkan +hw_module_t+ derivative is currently trivial. If support for multiple drivers is ever added, the HAL module will export a list of strings that can be passed to the module +open+ call. For the time being, only one driver is supported, and the constant string +HWVULKAN_DEVICE_0+ is passed to +open+.
-The Vulkan +hw_device_t+ derivative corresponds to a single driver, though that driver can support multiple physical devices. The +hw_device_t+ structure will be extended to export +vkGetGlobalExtensionProperties+, +vkCreateInstance+, and +vkGetInstanceProcAddr+ functions. The loader will find all other +VkInstance+, +VkPhysicalDevice+, and +vkGetDeviceProcAddr+ functions by calling +vkGetInstanceProcAddr+.
+The Vulkan +hw_device_t+ derivative corresponds to a single driver, though that driver can support multiple Vulkan physical devices. The +hw_device_t+ structure contains a function pointer for the +vkGetInstanceProcAddr+ function. The loader finds all other driver Vulkan functions by calling that +vkGetInstanceProcAddr+ function.
=== Layer Discovery and Loading ===
@@ -39,17 +37,15 @@
3. Injected layers, like framerate, social network, or game launcher overlays, which are provided by the user or some other application without the application's knowledge or consent. These violate Android's security policies and will not be supported.
-In the normal state the loader will only search in the application's native library directory for layers; details are TBD but it will probably just try to load any library with a name matching a particular pattern(e.g. +libvklayer_foo.so+). It will probably not need a separate manifest file; the developer deliberately included these layers, so the reasons to avoid loading libraries before enabling them don't apply.
+In the normal state the loader will only search in the application's normal library search path (as defined by the system ClassLoader) for layers. It will attempt to load any shared library named +libVkLayer_*.so+ as a layer library. Android does not use manifests to describe layers: because layers must have been deliberately included in the application by the developer, the motivation for manifests on other platforms don't apply.
-On debuggable devices (+ro.debuggable+ property exists and is non-zero, generally rooted or engineering builds) or debuggable processes (+prctl(PR_GET_DUMPABLE)==1+, based on the application's manifest), the loader may also search an adb-writeable location on /data for layers. It's not clear whether this is useful; in all the cases it could be used, the layer could be just as easily be put in the application's native library directory.
+On debuggable devices (+ro.debuggable+ property exists and is non-zero, generally rooted or engineering builds) the loader will also search the directory +/data/local/debug/vulkan+ and attempt to load layer libraries it finds there. This directory doesn't exist by default. On Android N and later, because this location is writable by adb, SELinux policies prevent mapping code located here as executable. So to use layers from here, SELinux enforcement must be disabled: +adb shell setenforce 0+. This mechanism is not intended for application developers, only for IHV and OEM engineers working on test devices that don't have private or sensitive data.
-Finally, the loader may include a built-in validation layer that it will enable based on settings in the Developer Options menu, which would send validation errors or warnings to the system log. Drivers may be able to emit additional hardware-specific errors/warnings through this mechanism. This layer would not be enumerated through the API. This is intended to allow cooperative end-users to collect extra information about failures from unmodified applications on unmodified devices to aid triage/diagnosis of difficult-to-reproduce problems. The functionality would be intentionally limited to minimize security and privacy risk.
-
-Our goal is to allow layers to be ported with only build-environment changes between Android and other platforms. This means the interface between layers and the loader must match the interface used by the LunarG loader. Currently, the LunarG interface has a few deficiencies and is largely unspecified. We intend to work with LunarG to correct as many deficiencies as we can and to specify the interface in detail so that layers can be implemented without referring to the loader source code.
+Our goal is to allow layers to be ported with only build-environment changes between Android and other platforms. For this to work, layers must properly implement things like +vkGetInstanceLayerProperties+ and +vkGetInstanceExtensionProperties+, even though the LunarG loader doesn't use them (it gets the information from manifests instead).
== Window System Integration ==
-The +vk_wsi_swapchin+ and +vk_wsi_device_swapchain+ extensions will primarily be implemented by the platform and live in +libvulkan.so+. The +VkSwapchain+ object and all interaction with +ANativeWindow+ will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver's +vkGetDeviceProcAddr+ functions, after passing through any enabled layers.
+The +vk_wsi_swapchin+ and +vk_wsi_device_swapchain+ extensions are primarily be implemented by the platform and live in +libvulkan.so+. The +VkSwapchain+ object and all interaction with +ANativeWindow+ will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver's +vkGetDeviceProcAddr+ functions, after passing through any enabled layers.
Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling
[source,c]
@@ -81,8 +77,6 @@
} VkNativeBufferANDROID;
----
-TBD: During swapchain re-creation (using +oldSwapChain+), we may have to defer allocation of new gralloc buffers until old buffers have been released. If so, the +vkCreateImage+ calls will be deferred until the first +vkAcquireNextImageWSI+ that would return the new image.
-
When creating a gralloc-backed image, the +VkImageCreateInfo+ will have:
----
.imageType = VK_IMAGE_TYPE_2D
@@ -162,4 +156,6 @@
* Added a VkFence parameter to vkAcquireImageANDROID corresponding to the
parameter added to vkAcquireNextImageKHR.
. *2016-01-08*
- * Added waitSemaphoreCount and pWaitSemaphores parameters to vkQueueSignalReleaseImageANDROID.
\ No newline at end of file
+ * Added waitSemaphoreCount and pWaitSemaphores parameters to vkQueueSignalReleaseImageANDROID.
+. *2016-06-17*
+ * Updates to reflect final behavior, closed some TBDs now that they've BDed.
\ No newline at end of file
diff --git a/vulkan/doc/implementors_guide/implementors_guide.html b/vulkan/doc/implementors_guide/implementors_guide.html
index 58ce0dc..0bfeb81 100644
--- a/vulkan/doc/implementors_guide/implementors_guide.html
+++ b/vulkan/doc/implementors_guide/implementors_guide.html
@@ -743,7 +743,6 @@
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>This document is intended for GPU IHVs writing Vulkan drivers for Android, and OEMs integrating them for specific devices. It describes how a Vulkan driver interacts with the system, how GPU-specific tools should be installed, and Android-specific requirements.</p></div>
-<div class="paragraph"><p>This is still a fairly rough draft; details will be filled in over time.</p></div>
</div>
</div>
<div class="sect1">
@@ -753,15 +752,15 @@
<div class="paragraph"><p>The NDK will include a stub <span class="monospaced">libvulkan.so</span> exporting the same symbols as the loader. Calling the Vulkan functions exported from <span class="monospaced">libvulkan.so</span> will enter trampoline functions in the loader which will dispatch to the appropriate layer or driver based on their first argument. The <span class="monospaced">vkGet*ProcAddr</span> calls will return the function pointers that the trampolines would dispatch to, so calling through these function pointers rather than the exported symbols will be slightly more efficient since it skips the trampoline and dispatch.</p></div>
<div class="sect2">
<h3 id="_driver_enumeration_and_loading">1.1. Driver Enumeration and Loading</h3>
-<div class="paragraph"><p>Android expects the GPUs available to the system to be known when the system image is built, so its driver enumeration process isn’t as elaborate as other platforms. The loader will use the existing HAL mechanism (see <a href="https://android.googlesource.com/platform/hardware/libhardware/+/lollipop-mr1-release/include/hardware/hardware.h">hardware.h</a>) for discovering and loading the driver. As of this writing, the preferred paths for 32-bit and 64-bit Vulkan drivers are:</p></div>
+<div class="paragraph"><p>Android expects the GPUs available to the system to be known when the system image is built, so its driver enumeration process isn’t as elaborate as on other platforms. The loader will use the existing HAL mechanism for discovering and loading the driver. As of this writing, the preferred paths for 32-bit and 64-bit Vulkan drivers are:</p></div>
<div class="literalblock">
<div class="content monospaced">
<pre>/vendor/lib/hw/vulkan.<ro.product.platform>.so
/vendor/lib64/hw/vulkan.<ro.product.platform>.so</pre>
</div></div>
-<div class="paragraph"><p>where <span class="monospaced"><ro.product.platform></span> is replaced by the value of the system property of that name. See <a href="https://android.googlesource.com/platform/hardware/libhardware/+/lollipop-mr1-release/hardware.c">libhardware/hardware.c</a> for details and supported alternative locations.</p></div>
+<div class="paragraph"><p>where <span class="monospaced"><ro.product.platform></span> is replaced by the value of the system property of that name. See <a href="https://android.googlesource.com/platform/hardware/libhardware/+/master/hardware.c">libhardware/hardware.c</a> for details and supported alternative locations.</p></div>
<div class="paragraph"><p>The Vulkan <span class="monospaced">hw_module_t</span> derivative is currently trivial. If support for multiple drivers is ever added, the HAL module will export a list of strings that can be passed to the module <span class="monospaced">open</span> call. For the time being, only one driver is supported, and the constant string <span class="monospaced">HWVULKAN_DEVICE_0</span> is passed to <span class="monospaced">open</span>.</p></div>
-<div class="paragraph"><p>The Vulkan <span class="monospaced">hw_device_t</span> derivative corresponds to a single driver, though that driver can support multiple physical devices. The <span class="monospaced">hw_device_t</span> structure will be extended to export <span class="monospaced">vkGetGlobalExtensionProperties</span>, <span class="monospaced">vkCreateInstance</span>, and <span class="monospaced">vkGetInstanceProcAddr</span> functions. The loader will find all other <span class="monospaced">VkInstance</span>, <span class="monospaced">VkPhysicalDevice</span>, and <span class="monospaced">vkGetDeviceProcAddr</span> functions by calling <span class="monospaced">vkGetInstanceProcAddr</span>.</p></div>
+<div class="paragraph"><p>The Vulkan <span class="monospaced">hw_device_t</span> derivative corresponds to a single driver, though that driver can support multiple Vulkan physical devices. The <span class="monospaced">hw_device_t</span> structure contains a function pointer for the <span class="monospaced">vkGetInstanceProcAddr</span> function. The loader finds all other driver Vulkan functions by calling that <span class="monospaced">vkGetInstanceProcAddr</span> function.</p></div>
</div>
<div class="sect2">
<h3 id="_layer_discovery_and_loading">1.2. Layer Discovery and Loading</h3>
@@ -784,17 +783,16 @@
</p>
</li>
</ol></div>
-<div class="paragraph"><p>In the normal state the loader will only search in the application’s native library directory for layers; details are TBD but it will probably just try to load any library with a name matching a particular pattern(e.g. <span class="monospaced">libvklayer_foo.so</span>). It will probably not need a separate manifest file; the developer deliberately included these layers, so the reasons to avoid loading libraries before enabling them don’t apply.</p></div>
-<div class="paragraph"><p>On debuggable devices (<span class="monospaced">ro.debuggable</span> property exists and is non-zero, generally rooted or engineering builds) or debuggable processes (<span class="monospaced">prctl(PR_GET_DUMPABLE)==1</span>, based on the application’s manifest), the loader may also search an adb-writeable location on /data for layers. It’s not clear whether this is useful; in all the cases it could be used, the layer could be just as easily be put in the application’s native library directory.</p></div>
-<div class="paragraph"><p>Finally, the loader may include a built-in validation layer that it will enable based on settings in the Developer Options menu, which would send validation errors or warnings to the system log. Drivers may be able to emit additional hardware-specific errors/warnings through this mechanism. This layer would not be enumerated through the API. This is intended to allow cooperative end-users to collect extra information about failures from unmodified applications on unmodified devices to aid triage/diagnosis of difficult-to-reproduce problems. The functionality would be intentionally limited to minimize security and privacy risk.</p></div>
-<div class="paragraph"><p>Our goal is to allow layers to be ported with only build-environment changes between Android and other platforms. This means the interface between layers and the loader must match the interface used by the LunarG loader. Currently, the LunarG interface has a few deficiencies and is largely unspecified. We intend to work with LunarG to correct as many deficiencies as we can and to specify the interface in detail so that layers can be implemented without referring to the loader source code.</p></div>
+<div class="paragraph"><p>In the normal state the loader will only search in the application’s normal library search path (as defined by the system ClassLoader) for layers. It will attempt to load any shared library named <span class="monospaced">libVkLayer_*.so</span> as a layer library. Android does not use manifests to describe layers: because layers must have been deliberately included in the application by the developer, the motivation for manifests on other platforms don’t apply.</p></div>
+<div class="paragraph"><p>On debuggable devices (<span class="monospaced">ro.debuggable</span> property exists and is non-zero, generally rooted or engineering builds) the loader will also search the directory <span class="monospaced">/data/local/debug/vulkan</span> and attempt to load layer libraries it finds there. This directory doesn’t exist by default. On Android N and later, because this location is writable by adb, SELinux policies prevent mapping code located here as executable. So to use layers from here, SELinux enforcement must be disabled: <span class="monospaced">adb shell setenforce 0</span>. This mechanism is not intended for application developers, only for IHV and OEM engineers working on test devices that don’t have private or sensitive data.</p></div>
+<div class="paragraph"><p>Our goal is to allow layers to be ported with only build-environment changes between Android and other platforms. For this to work, layers must properly implement things like <span class="monospaced">vkGetInstanceLayerProperties</span> and <span class="monospaced">vkGetInstanceExtensionProperties</span>, even though the LunarG loader doesn’t use them (it gets the information from manifests instead).</p></div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_window_system_integration">2. Window System Integration</h2>
<div class="sectionbody">
-<div class="paragraph"><p>The <span class="monospaced">vk_wsi_swapchin</span> and <span class="monospaced">vk_wsi_device_swapchain</span> extensions will primarily be implemented by the platform and live in <span class="monospaced">libvulkan.so</span>. The <span class="monospaced">VkSwapchain</span> object and all interaction with <span class="monospaced">ANativeWindow</span> will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver’s <span class="monospaced">vkGetDeviceProcAddr</span> functions, after passing through any enabled layers.</p></div>
+<div class="paragraph"><p>The <span class="monospaced">vk_wsi_swapchin</span> and <span class="monospaced">vk_wsi_device_swapchain</span> extensions are primarily be implemented by the platform and live in <span class="monospaced">libvulkan.so</span>. The <span class="monospaced">VkSwapchain</span> object and all interaction with <span class="monospaced">ANativeWindow</span> will be handled by the platform and not exposed to drivers. The WSI implementation will rely on a few private interfaces to the driver for this implementation. These will be loaded through the driver’s <span class="monospaced">vkGetDeviceProcAddr</span> functions, after passing through any enabled layers.</p></div>
<div class="paragraph"><p>Implementations may need swapchain buffers to be allocated with implementation-defined private gralloc usage flags. When creating a swapchain, the platform will ask the driver to translate the requested format and image usage flags into gralloc usage flags by calling</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 3.1.8
@@ -826,7 +824,6 @@
<span style="color: #009900">int</span> format<span style="color: #990000">;</span>
<span style="color: #009900">int</span> usage<span style="color: #990000">;</span>
<span style="color: #FF0000">}</span> VkNativeBufferANDROID<span style="color: #990000">;</span></tt></pre></div></div>
-<div class="paragraph"><p>TBD: During swapchain re-creation (using <span class="monospaced">oldSwapChain</span>), we may have to defer allocation of new gralloc buffers until old buffers have been released. If so, the <span class="monospaced">vkCreateImage</span> calls will be deferred until the first <span class="monospaced">vkAcquireNextImageWSI</span> that would return the new image.</p></div>
<div class="paragraph"><p>When creating a gralloc-backed image, the <span class="monospaced">VkImageCreateInfo</span> will have:</p></div>
<div class="listingblock">
<div class="content monospaced">
@@ -969,6 +966,18 @@
</li>
</ul></div>
</li>
+<li>
+<p>
+<strong>2016-06-17</strong>
+</p>
+<div class="ulist"><ul>
+<li>
+<p>
+Updates to reflect final behavior, closed some TBDs now that they’ve BDed.
+</p>
+</li>
+</ul></div>
+</li>
</ol></div>
</div>
</div>
@@ -977,7 +986,7 @@
<div id="footer">
<div id="footer-text">
Version 5<br>
-Last updated 2016-01-08 22:43:07 PST
+Last updated 2016-06-17 13:54:25 PDT
</div>
</div>
</body>