Merge "Avoid unlinking app image file"
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
new file mode 100644
index 0000000..fc43250
--- /dev/null
+++ b/cmds/dumpstate/bugreport-format.md
@@ -0,0 +1,87 @@
+# Bugreport file format
+
+This document specifies the format of the bugreport files generated by the
+bugreport services (like `bugreport` and `bugreportplus`) and delivered to the
+end user (i.e., it doesn’t include other tools like `adb bugreport`).
+
+A _bugreport_ is initially generated by dumpstate, then processed by **Shell**,
+which in turn delivers it to the end user through a `ACTION_SEND_MULTIPLE`
+intent; the end user then select which app (like an email client) handles such
+intent.
+
+## Text file (Pre-M)
+Prior to _Android M (Marshmallow)_, `dumpstate` generates a flat .txt file named
+_bugreport-DATE.txt_ (where _DATE_ is date the bugreport was generated, in the
+format _YYYY-MM-DD-HH-MM-SS_), and Shell simply propagates it as an attachment
+in the `ACTION_SEND_MULTIPLE` intent.
+
+## Version v0 (Android M)
+On _Android M (Marshmallow)_, dumpstate still generates a flat
+_bugreport-DATE.txt_ file, but then **Shell** creates a zip file called
+_bugreport-DATE.zip_ containing a _bugreport-DATE.txt_ entry and sends that
+file as the `ACTION_SEND_MULTIPLE` attachment.
+
+## Version v1 (Android N)
+On _Android N (TBD)_, `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).
+
+The zip file is by default called _bugreport-DATE.zip_ and it contains a
+_bugreport-DATE.txt_ entry, although the end user can change the name (through
+**Shell**), in which case they would be called _bugreport-NEW_NAME.zip_ and
+_bugreport-NEW_NAME.txt_ respectively.
+
+The zip file also contains 2 metadata entries generated by `dumpstate`:
+
+- `version.txt`: whose value is **v1**.
+- `main-entry.txt`: whose value is the name of the flat text entry (i.e.,
+ _bugreport-DATE.txt_ or _bugreport-NEW_NAME.txt_).
+
+`dumpstate` can also copy files from the device’s filesystem into the zip file
+under the `FS` folder. For example, a `/dirA/dirB/fileC` file in the device
+would generate a `FS/dirA/dirB/fileC` entry in the zip file.
+
+The flat file also has some minor changes:
+
+- Tombstone files were removed and added to the zip file.
+- The duration of each section is printed in the report.
+- Some dumpsys sections (memory and cpuinfo) are reported earlier in the file.
+
+Besides the files generated by `dumpstate`, **Shell** can also add 2 other
+files upon the end user’s request:
+
+- `title.txt`: whose value is a single-line summary of the problem.
+- `description.txt`: whose value is a multi-line, detailed description of the problem.
+
+## Intermediate versions
+During development, the versions will be suffixed with _-devX_ or
+_-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the
+changes become stable.
+
+For example, the initial version during _Android N_ development was
+**v1-dev1**. When `dumpsys` was split in 2 sections but not all tools were
+ready to parse that format, the version was named **v1-dev1-dumpsys-split**,
+which had to be passed do `dumpsys` explicitly (i.e., trhough a
+`-V v1-dev1-dumpsys-split` argument). Once that format became stable and tools
+knew how to parse it, the default version became **v1-dev2**.
+
+Similarly, if changes in the file format are made after the initial release of
+Android defining that format, then a new _sub-version_ will be used.
+For example, if after _Android N_ launches changes are made for the next _N_
+release, the version will be called **v1.1** or something like that.
+
+Determining version and main entry
+-----------------------------------------------
+
+Tools parsing the zipped bugreport file can use the following algorithm to
+determine the bugreport format version and its main entry:
+
+```
+If [entries contain "version.txt"]
+ version = read("version.txt")
+ main_entry = read("main_entry.txt")
+else
+ version = v0
+ main_entry = entries[0]
+fi
+```
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 2f124ac..22fd2c3 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -83,6 +83,16 @@
// Root dir for all files copied as-is into the bugreport
const std::string& ZIP_ROOT_DIR = "FS";
+/*
+ * List of supported zip format versions.
+ *
+ * See bugreport-format.txt for more info.
+ */
+// TODO: change to "v1" before final N build
+static std::string VERSION_DEFAULT = "v1-dev1";
+// TODO: remove before final N build
+static std::string VERSION_DUMPSYS_SPLIT = "v1-dev1-dumpsys-split";
+
/* gets the tombstone data, according to the bugreport type: if zipped gets all tombstones,
* otherwise gets just those modified in the last half an hour. */
static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
@@ -132,9 +142,9 @@
if (!zip_writer) return;
const char *title = "MOUNT INFO";
mount_points.clear();
- DurationReporter duration_reporter(title);
+ DurationReporter duration_reporter(title, NULL);
for_each_pid(do_mountinfo, NULL);
- printf("%s: %d entries added to zip file\n", title, mount_points.size());
+ ALOGD("%s: %lu entries added to zip file\n", title, mount_points.size());
}
static void dump_dev_files(const char *title, const char *driverpath, const char *filename)
@@ -325,7 +335,7 @@
/* End copy from system/core/logd/LogBuffer.cpp */
/* dumps the current system state to stdout */
-static void print_header() {
+static void print_header(std::string version) {
char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
char network[PROPERTY_VALUE_MAX], date[80];
@@ -352,17 +362,22 @@
printf("Kernel: ");
dump_file(NULL, "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
+ printf("Bugreport format version: %s\n", version.c_str());
printf("\n");
}
/* adds a new entry to the existing zip file. */
static bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
- DurationReporter duration_reporter(("ADD ZIP ENTRY " + entry_name).c_str());
+ if (!zip_writer) {
+ ALOGD("Not adding zip entry %s from fd because zip_writer is not set", entry_name.c_str());
+ return false;
+ }
ALOGD("Adding zip entry %s", entry_name.c_str());
int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(),
ZipWriter::kCompress, get_mtime(fd, now));
if (err) {
- ALOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(), ZipWriter::ErrorCodeString(err));
+ ALOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
@@ -409,11 +424,44 @@
/* adds all files from a directory to the zipped bugreport file */
void add_dir(const char *dir, bool recursive) {
- if (!zip_writer) return;
- DurationReporter duration_reporter(dir);
+ if (!zip_writer) {
+ ALOGD("Not adding dir %s because zip_writer is not set", dir);
+ return;
+ }
+ DurationReporter duration_reporter(dir, NULL);
dump_files(NULL, dir, recursive ? skip_none : is_dir, _add_file_from_fd);
}
+/* adds a text entry entry to the existing zip file. */
+static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
+ if (!zip_writer) {
+ ALOGD("Not adding text zip entry %s because zip_writer is not set", entry_name.c_str());
+ return false;
+ }
+ ALOGD("Adding zip text entry %s", entry_name.c_str());
+ int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, now);
+ if (err) {
+ ALOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
+ return false;
+ }
+
+ err = zip_writer->WriteBytes(content.c_str(), content.length());
+ if (err) {
+ ALOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
+ return false;
+ }
+
+ err = zip_writer->FinishEntry();
+ if (err) {
+ ALOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ return false;
+ }
+
+ return true;
+}
+
static void dumpstate(const std::string& screenshot_path) {
DurationReporter duration_reporter("DUMPSTATE");
unsigned long timeout;
@@ -717,24 +765,28 @@
printf("========================================================\n");
+ printf("== Final progress (pid %d): %d/%d (originally %d)\n",
+ getpid(), progress, weight_total, WEIGHT_TOTAL);
+ printf("========================================================\n");
printf("== dumpstate: done\n");
printf("========================================================\n");
}
static void usage() {
- fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s] [-q]\n"
- " -o: write to file (instead of stdout)\n"
- " -d: append date to filename (requires -o)\n"
- " -z: generates zipped file (requires -o)\n"
- " -p: capture screenshot to filename.png (requires -o)\n"
- " -s: write output to control socket (for init)\n"
+ fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s] [-q] [-B] [-P] [-R] [-V version]\n"
" -b: play sound file instead of vibrate, at beginning of job\n"
" -e: play sound file instead of vibrate, at end of job\n"
+ " -o: write to file (instead of stdout)\n"
+ " -d: append date to filename (requires -o)\n"
+ " -p: capture screenshot to filename.png (requires -o)\n"
+ " -z: generates zipped file (requires -o)\n"
+ " -s: write output to control socket (for init)\n"
" -q: disable vibrate\n"
" -B: send broadcast when finished (requires -o)\n"
- " -P: send broadacast when started and update system properties on progress (requires -o and -B)\n"
+ " -P: send broadcast when started and update system properties on progress (requires -o and -B)\n"
" -R: take bugreport in remote mode (requires -o, -z, -d and -B, shouldn't be used with -P)\n"
- );
+ " -V: sets the bugreport format version (%s or %s)\n",
+ VERSION_DEFAULT.c_str(), VERSION_DUMPSYS_SPLIT.c_str());
}
static void sigpipe_handler(int n) {
@@ -751,6 +803,10 @@
ALOGE("Failed to add text entry to .zip file\n");
return false;
}
+ if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
+ ALOGE("Failed to add main_entry.txt to .zip file\n");
+ return false;
+ }
int32_t err = zip_writer->Finish();
if (err) {
@@ -799,6 +855,48 @@
return std::string(hash_buffer);
}
+/* switch to non-root user and group */
+bool drop_root() {
+ /* ensure we will keep capabilities when we drop root */
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
+ AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC };
+ if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
+ ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
+ return false;
+ }
+ if (setgid(AID_SHELL) != 0) {
+ ALOGE("Unable to setgid, aborting: %s\n", strerror(errno));
+ return false;
+ }
+ if (setuid(AID_SHELL) != 0) {
+ ALOGE("Unable to setuid, aborting: %s\n", strerror(errno));
+ return false;
+ }
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
int main(int argc, char *argv[]) {
struct sigaction sigact;
@@ -811,6 +909,7 @@
int do_broadcast = 0;
int do_early_screenshot = 0;
int is_remote_mode = 0;
+ std::string version = VERSION_DEFAULT;
now = time(NULL);
@@ -840,7 +939,7 @@
/* parse arguments */
int c;
- while ((c = getopt(argc, argv, "dho:svqzpPBR")) != -1) {
+ while ((c = getopt(argc, argv, "dho:svqzpPBRV:")) != -1) {
switch (c) {
case 'd': do_add_date = 1; break;
case 'z': do_zip_file = 1; break;
@@ -852,6 +951,7 @@
case 'P': do_update_progress = 1; break;
case 'R': is_remote_mode = 1; break;
case 'B': do_broadcast = 1; break;
+ case 'V': version = optarg; break;
case '?': printf("\n");
case 'h':
usage();
@@ -874,6 +974,13 @@
exit(1);
}
+ if (version != VERSION_DEFAULT && version != VERSION_DUMPSYS_SPLIT) {
+ usage();
+ exit(1);
+ }
+
+ ALOGI("bugreport format version: %s\n", version.c_str());
+
do_early_screenshot = do_update_progress;
// If we are going to use a socket, do it as early as possible
@@ -932,6 +1039,7 @@
if (do_zip_file) {
ALOGD("Creating initial .zip file");
path = bugreport_dir + "/" + base_name + "-" + suffix + ".zip";
+ create_parent_dirs(path.c_str());
zip_file.reset(fopen(path.c_str(), "wb"));
if (!zip_file) {
ALOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno));
@@ -939,6 +1047,7 @@
} else {
zip_writer.reset(new ZipWriter(zip_file.get()));
}
+ add_text_zip_entry("version.txt", version);
}
if (do_update_progress) {
@@ -959,8 +1068,6 @@
fclose(cmdline);
}
- print_header();
-
/* open the vibrator before dropping root */
std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
if (do_vibrate) {
@@ -999,42 +1106,7 @@
add_dir(RECOVERY_DIR, true);
add_mountinfo();
- /* ensure we will keep capabilities when we drop root */
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
- return -1;
- }
-
- /* switch to non-root user and group */
- gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW,
- AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC };
- if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
- ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
- return -1;
- }
- if (setgid(AID_SHELL) != 0) {
- ALOGE("Unable to setgid, aborting: %s\n", strerror(errno));
- return -1;
- }
- if (setuid(AID_SHELL) != 0) {
- ALOGE("Unable to setuid, aborting: %s\n", strerror(errno));
- return -1;
- }
-
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
- capdata[0].inheritable = 0;
- capdata[1].inheritable = 0;
-
- if (capset(&capheader, &capdata[0]) < 0) {
- ALOGE("capset failed: %s\n", strerror(errno));
+ if (!drop_root()) {
return -1;
}
@@ -1044,6 +1116,10 @@
directly, but the libziparchive doesn't support that option yet. */
redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
}
+ // NOTE: there should be no stdout output until now, otherwise it would break the header.
+ // In particular, DurationReport objects should be created passing 'title, NULL', so their
+ // duration is logged into ALOG instead.
+ print_header(version);
dumpstate(do_early_screenshot ? "": screenshot_path);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 0a9f9e2..a6afbf4 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -112,6 +112,9 @@
/* redirect output to a file */
void redirect_to_file(FILE *redirect, char *path);
+/* create leading directories, if necessary */
+void create_parent_dirs(const char *path);
+
/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
const char *dump_traces();
@@ -165,6 +168,7 @@
class DurationReporter {
public:
DurationReporter(const char *title);
+ DurationReporter(const char *title, FILE* out);
~DurationReporter();
@@ -172,6 +176,7 @@
private:
const char* title_;
+ FILE* out_;
uint64_t started_;
};
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 3f2d126..0c35430 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -60,19 +60,26 @@
NULL,
};
-DurationReporter::DurationReporter(const char *title) {
+DurationReporter::DurationReporter(const char *title) : DurationReporter(title, stdout) {}
+
+DurationReporter::DurationReporter(const char *title, FILE *out) {
title_ = title;
if (title) {
started_ = DurationReporter::nanotime();
}
+ out_ = out;
}
DurationReporter::~DurationReporter() {
if (title_) {
uint64_t elapsed = DurationReporter::nanotime() - started_;
// Use "Yoda grammar" to make it easier to grep|sort sections.
- printf("------ %.3fs was the duration of '%s' ------\n",
- (float) elapsed / NANOS_PER_SEC, title_);
+ if (out_) {
+ fprintf(out_, "------ %.3fs was the duration of '%s' ------\n",
+ (float) elapsed / NANOS_PER_SEC, title_);
+ } else {
+ ALOGD("Duration of '%s': %.3fs\n", title_, (float) elapsed / NANOS_PER_SEC);
+ }
}
}
@@ -650,25 +657,37 @@
close(fd);
}
-/* redirect output to a file */
-void redirect_to_file(FILE *redirect, char *path) {
- char *chp = path;
+void create_parent_dirs(const char *path) {
+ char *chp = (char*) path;
/* skip initial slash */
if (chp[0] == '/')
chp++;
/* create leading directories, if necessary */
+ struct stat dir_stat;
while (chp && chp[0]) {
chp = strchr(chp, '/');
if (chp) {
*chp = 0;
- mkdir(path, 0770); /* drwxrwx--- */
+ if (stat(path, &dir_stat) == -1 || !S_ISDIR(dir_stat.st_mode)) {
+ ALOGI("Creating directory %s\n", path);
+ if (mkdir(path, 0770)) { /* drwxrwx--- */
+ ALOGE("Unable to create directory %s: %s\n", path, strerror(errno));
+ } else if (chown(path, AID_SHELL, AID_SHELL)) {
+ ALOGE("Unable to change ownership of dir %s: %s\n", path, strerror(errno));
+ }
+ }
*chp++ = '/';
}
}
+}
- int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+/* redirect output to a file */
+void redirect_to_file(FILE *redirect, char *path) {
+ create_parent_dirs(path);
+
+ int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if (fd < 0) {
fprintf(stderr, "%s: %s\n", path, strerror(errno));
@@ -690,7 +709,7 @@
/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
const char *dump_traces() {
- DurationReporter duration_reporter("DUMP TRACES");
+ DurationReporter duration_reporter("DUMP TRACES", NULL);
ON_DRY_RUN_RETURN(NULL);
const char* result = NULL;
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 209632e..65bcf39 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -43,6 +43,43 @@
LOCAL_CLANG := true
include $(BUILD_EXECUTABLE)
+#
+# OTA Executable
+#
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := otapreopt
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(common_cflags)
+
+# Base & ASLR boundaries for boot image creation.
+ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
+ LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := -0x1000000
+else
+ LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
+endif
+ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA
+ LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := 0x1000000
+else
+ LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
+endif
+LOCAL_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS)
+LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
+LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
+
+LOCAL_SRC_FILES := otapreopt.cpp $(common_src_files)
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libcutils \
+ liblog \
+ liblogwrap \
+ libselinux \
+
+LOCAL_STATIC_LIBRARIES := libdiskusage
+LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+LOCAL_CLANG := true
+include $(BUILD_EXECUTABLE)
+
# Tests.
include $(LOCAL_PATH)/tests/Android.mk
\ No newline at end of file
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 02e9601..c0ae5b7 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -211,12 +211,41 @@
return destroy_app_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]));
}
-static int do_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
+static int do_ota_dexopt(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED) {
+ // Time to fork and run otapreopt.
+ pid_t pid = fork();
+ if (pid == 0) {
+ const char* argv[1 + 9 + 1];
+ argv[0] = "/system/bin/otapreopt";
+ for (size_t i = 1; i <= 9; ++i) {
+ argv[i] = arg[i - 1];
+ }
+ argv[10] = nullptr;
+
+ execv(argv[0], (char * const *)argv);
+ ALOGE("execv(OTAPREOPT) failed: %s\n", strerror(errno));
+ exit(99);
+ } else {
+ int res = wait_child(pid);
+ if (res == 0) {
+ ALOGV("DexInv: --- END OTAPREOPT (success) ---\n");
+ } else {
+ ALOGE("DexInv: --- END OTAPREOPT --- status=0x%04x, process failed\n", res);
+ }
+ return res;
+ }
+}
+
+static int do_dexopt(char **arg, char reply[REPLY_MAX])
{
+ int dexopt_flags = atoi(arg[6]);
+ if ((dexopt_flags & DEXOPT_OTA) != 0) {
+ return do_ota_dexopt(arg, reply);
+ }
/* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags, volume_uuid,
use_profiles */
return dexopt(arg[0], atoi(arg[1]), arg[2], arg[3], atoi(arg[4]),
- arg[5], atoi(arg[6]), parse_null(arg[7]), (atoi(arg[8]) == 0 ? false : true));
+ arg[5], dexopt_flags, parse_null(arg[7]), (atoi(arg[8]) == 0 ? false : true));
}
static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 18f998d..0d21519 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -48,6 +48,7 @@
// 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";
@@ -74,6 +75,7 @@
constexpr int DEXOPT_DEBUGGABLE = 1 << 3;
constexpr int DEXOPT_BOOTCOMPLETE = 1 << 4;
constexpr int DEXOPT_EXTRACTONLY = 1 << 5;
+constexpr int DEXOPT_OTA = 1 << 6;
/* all known values for dexopt flags */
constexpr int DEXOPT_MASK =
@@ -81,7 +83,8 @@
| DEXOPT_SAFEMODE
| DEXOPT_DEBUGGABLE
| DEXOPT_BOOTCOMPLETE
- | DEXOPT_EXTRACTONLY;
+ | DEXOPT_EXTRACTONLY
+ | DEXOPT_OTA;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
new file mode 100644
index 0000000..27f7939
--- /dev/null
+++ b/cmds/installd/otapreopt.cpp
@@ -0,0 +1,641 @@
+/*
+ ** Copyright 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.
+ */
+
+#include <algorithm>
+#include <inttypes.h>
+#include <random>
+#include <selinux/android.h>
+#include <selinux/avc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <cutils/fs.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+
+#include <commands.h>
+#include <globals.h>
+#include <installd_deps.h> // Need to fill in requirements of commands.
+#include <string_helpers.h>
+#include <system_properties.h>
+#include <utils.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "otapreopt"
+#endif
+
+#define BUFFER_MAX 1024 /* input buffer for commands */
+#define TOKEN_MAX 16 /* max number of arguments in buffer */
+#define REPLY_MAX 256 /* largest reply allowed */
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+static constexpr const char* kBootClassPathPropertyName = "env.BOOTCLASSPATH";
+static constexpr const char* kAndroidRootPathPropertyName = "env.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);
+}
+
+template<typename T>
+static constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) {
+ return RoundDown(x + n - 1, n);
+}
+
+class OTAPreoptService {
+ public:
+ static constexpr const char* kOTADataDirectory = "/data/ota";
+
+ // Main driver. Performs the following steps.
+ //
+ // 1) Parse options (read system properties etc from B partition).
+ //
+ // 2) Read in package data.
+ //
+ // 3) Prepare environment variables.
+ //
+ // 4) Prepare(compile) boot image, if necessary.
+ //
+ // 5) Run update.
+ int Main(int argc, char** argv) {
+ if (!ReadSystemProperties()) {
+ LOG(ERROR)<< "Failed reading system properties.";
+ return 1;
+ }
+
+ if (!ReadEnvironment()) {
+ LOG(ERROR) << "Failed reading environment properties.";
+ return 2;
+ }
+
+ if (!ReadPackage(argc, argv)) {
+ LOG(ERROR) << "Failed reading command line file.";
+ return 3;
+ }
+
+ PrepareEnvironment();
+
+ if (!PrepareBootImage()) {
+ LOG(ERROR) << "Failed preparing boot image.";
+ return 4;
+ }
+
+ int dexopt_retcode = RunPreopt();
+
+ return dexopt_retcode;
+ }
+
+ int GetProperty(const char* key, char* value, const char* default_value) {
+ const std::string* prop_value = system_properties_.GetProperty(key);
+ if (prop_value == nullptr) {
+ if (default_value == nullptr) {
+ return 0;
+ }
+ // Copy in the default value.
+ strncpy(value, default_value, kPropertyValueMax - 1);
+ value[kPropertyValueMax - 1] = 0;
+ return strlen(default_value);// TODO: Need to truncate?
+ }
+ size_t size = std::min(kPropertyValueMax - 1, prop_value->length());
+ strncpy(value, prop_value->data(), size);
+ value[size] = 0;
+ return static_cast<int>(size);
+ }
+
+private:
+ bool ReadSystemProperties() {
+ // TODO(agampe): What to do about the things in default.prop? It's only heap sizes, so it's easy
+ // to emulate for now, but has issues (e.g., vendors modifying the boot classpath
+ // may require larger values here - revisit). That's why this goes first, so that
+ // if those dummy values are overridden in build.prop, that's what we'll get.
+ //
+ // Note: It seems we'll get access to the B root partition, so we should read the default.prop
+ // file.
+ // if (!system_properties_.Load(b_mount_path_ + "/default.prop") {
+ // return false;
+ // }
+ system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xms", "64m");
+ system_properties_.SetProperty("dalvik.vm.image-dex2oat-Xmx", "64m");
+ system_properties_.SetProperty("dalvik.vm.dex2oat-Xms", "64m");
+ system_properties_.SetProperty("dalvik.vm.dex2oat-Xmx", "512m");
+
+ // TODO(agampe): Do this properly/test.
+ return system_properties_.Load(b_mount_path_ + "/system/build.prop");
+ }
+
+ bool ReadEnvironment() {
+ // Read important environment variables. For simplicity, store them as
+ // system properties.
+ // TODO(agampe): We'll have to parse init.environ.rc for BOOTCLASSPATH.
+ // For now, just the A version.
+ const char* boot_classpath = getenv("BOOTCLASSPATH");
+ if (boot_classpath == nullptr) {
+ return false;
+ }
+ system_properties_.SetProperty(kBootClassPathPropertyName, boot_classpath);
+
+ const char* root_path = getenv("ANDROID_ROOT");
+ if (root_path == nullptr) {
+ return false;
+ }
+ system_properties_.SetProperty(kAndroidRootPathPropertyName, b_mount_path_ + root_path);
+
+ return true;
+ }
+
+ bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) {
+ size_t index = 0;
+ while (index < ARRAY_SIZE(package_parameters_) &&
+ argv[index + 1] != nullptr) {
+ package_parameters_[index] = argv[index + 1];
+ index++;
+ }
+ if (index != ARRAY_SIZE(package_parameters_)) {
+ LOG(ERROR) << "Wrong number of parameters";
+ return false;
+ }
+
+ return true;
+ }
+
+ 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()));
+
+ for (const std::string& e : environ_) {
+ putenv(const_cast<char*>(e.c_str()));
+ }
+ }
+
+ // Ensure that we have the right boot image. The first time any app is
+ // compiled, we'll try to generate it.
+ bool PrepareBootImage() {
+ if (package_parameters_[kISAIndex] == nullptr) {
+ LOG(ERROR) << "Instruction set missing.";
+ return false;
+ }
+ const char* isa = package_parameters_[kISAIndex];
+
+ // Check whether the file exists where expected.
+ std::string dalvik_cache = std::string(kOTADataDirectory) + "/" + 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;
+ }
+
+ // 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";
+ return false;
+ }
+ }
+ if (access(isa_path.c_str(), F_OK) != 0) {
+ if (mkdir(isa_path.c_str(), 0711) != 0) {
+ PLOG(ERROR) << "Could not create dalvik-cache isa dir";
+ return false;
+ }
+ }
+
+ // Prepare and run dex2oat.
+ // TODO: Delete files, just for a blank slate.
+ const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName);
+
+ // 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(b_mount_path_ + "/system/bin/dex2oat");
+ cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
+ for (const std::string& boot_part : Split(boot_cp, ':')) {
+ cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
+ }
+ cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
+
+ int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
+ ART_BASE_ADDRESS_MAX_DELTA);
+ cmd.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + base_offset));
+
+ cmd.push_back(StringPrintf("--instruction-set=%s", isa));
+
+ // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp.
+ AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms",
+ "-Xms",
+ true,
+ cmd);
+ AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx",
+ "-Xmx",
+ true,
+ cmd);
+ AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter",
+ "--compiler-filter=",
+ false,
+ cmd);
+ cmd.push_back(StringPrintf("--image-classes=%s/system/etc/preloaded-classes",
+ b_mount_path_.c_str()));
+ // TODO: Compiled-classes.
+ const std::string* extra_opts =
+ system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
+ if (extra_opts != nullptr) {
+ std::vector<std::string> extra_vals = Split(*extra_opts, ' ');
+ cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
+ }
+ // TODO: Should we lower this? It's usually set close to max, because
+ // normally there's not much else going on at boot.
+ AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads",
+ "-j",
+ false,
+ cmd);
+ AddCompilerOptionFromSystemProperty(
+ StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
+ "--instruction-set-variant=",
+ false,
+ cmd);
+ AddCompilerOptionFromSystemProperty(
+ StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(),
+ "--instruction-set-features=",
+ false,
+ cmd);
+
+ std::string error_msg;
+ bool result = Exec(cmd, &error_msg);
+ if (!result) {
+ LOG(ERROR) << "Could not generate boot image: " << error_msg;
+ }
+ return result;
+ }
+
+ static const char* ParseNull(const char* arg) {
+ return (strcmp(arg, "!") == 0) ? nullptr : arg;
+ }
+
+ int RunPreopt() {
+ /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags,
+ volume_uuid, use_profiles */
+ int ret = dexopt(package_parameters_[0],
+ atoi(package_parameters_[1]),
+ package_parameters_[2],
+ package_parameters_[3],
+ atoi(package_parameters_[4]),
+ package_parameters_[5],
+ atoi(package_parameters_[6]),
+ ParseNull(package_parameters_[7]),
+ (atoi(package_parameters_[8]) == 0 ? false : true));
+ return ret;
+ }
+
+ ////////////////////////////////////
+ // Helpers, mostly taken from ART //
+ ////////////////////////////////////
+
+ // Wrapper on fork/execv to run a command in a subprocess.
+ 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;
+
+ // Convert the args to char pointers.
+ const char* program = arg_vector[0].c_str();
+ std::vector<char*> args;
+ for (size_t i = 0; i < arg_vector.size(); ++i) {
+ const std::string& arg = arg_vector[i];
+ char* arg_str = const_cast<char*>(arg.c_str());
+ CHECK(arg_str != nullptr) << i;
+ args.push_back(arg_str);
+ }
+ args.push_back(nullptr);
+
+ // Fork and exec.
+ pid_t pid = fork();
+ if (pid == 0) {
+ // No allocation allowed between fork and exec.
+
+ // Change process groups, so we don't get reaped by ProcessManager.
+ setpgid(0, 0);
+
+ execv(program, &args[0]);
+
+ PLOG(ERROR) << "Failed to execv(" << command_line << ")";
+ // _exit to avoid atexit handlers in child.
+ _exit(1);
+ } else {
+ if (pid == -1) {
+ *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
+ command_line.c_str(), strerror(errno));
+ return false;
+ }
+
+ // wait for subprocess to finish
+ int status;
+ pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+ if (got_pid != pid) {
+ *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
+ "wanted %d, got %d: %s",
+ command_line.c_str(), pid, got_pid, strerror(errno));
+ return false;
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+ command_line.c_str());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc.
+ static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
+ constexpr size_t kPageSize = PAGE_SIZE;
+ CHECK_EQ(min_delta % kPageSize, 0u);
+ CHECK_EQ(max_delta % kPageSize, 0u);
+ CHECK_LT(min_delta, max_delta);
+
+ std::default_random_engine generator;
+ generator.seed(GetSeed());
+ std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
+ int32_t r = distribution(generator);
+ if (r % 2 == 0) {
+ r = RoundUp(r, kPageSize);
+ } else {
+ r = RoundDown(r, kPageSize);
+ }
+ CHECK_LE(min_delta, r);
+ CHECK_GE(max_delta, r);
+ CHECK_EQ(r % kPageSize, 0u);
+ return r;
+ }
+
+ static uint64_t GetSeed() {
+#ifdef __BIONIC__
+ // Bionic exposes arc4random, use it.
+ uint64_t random_data;
+ arc4random_buf(&random_data, sizeof(random_data));
+ return random_data;
+#else
+#error "This is only supposed to run with bionic. Otherwise, implement..."
+#endif
+ }
+
+ 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);
+ if (value != nullptr) {
+ if (runtime) {
+ out.push_back("--runtime-arg");
+ }
+ if (prefix != nullptr) {
+ out.push_back(StringPrintf("%s%s", prefix, value->c_str()));
+ } else {
+ out.push_back(*value);
+ }
+ }
+ }
+
+ // The path where the B partitions are mounted.
+ // TODO(agampe): If we're running this *inside* the change-root, we wouldn't need this.
+ std::string b_mount_path_;
+
+ // 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_;
+
+ const char* package_parameters_[9];
+
+ // Store environment values we need to set.
+ std::vector<std::string> environ_;
+};
+
+OTAPreoptService gOps;
+
+////////////////////////
+// Plug-in functions. //
+////////////////////////
+
+int get_property(const char *key, char *value, const char *default_value) {
+ // TODO: Replace with system-properties map.
+ return gOps.GetProperty(key, value, default_value);
+}
+
+// Compute the output path of
+bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir,
+ const char *apk_path,
+ const char *instruction_set) {
+ // TODO: Insert B directory.
+ char *file_name_start;
+ char *file_name_end;
+
+ file_name_start = strrchr(apk_path, '/');
+ if (file_name_start == nullptr) {
+ ALOGE("apk_path '%s' has no '/'s in it\n", apk_path);
+ return false;
+ }
+ file_name_end = strrchr(file_name_start, '.');
+ if (file_name_end == nullptr) {
+ ALOGE("apk_path '%s' has no extension\n", apk_path);
+ return false;
+ }
+
+ // Calculate file_name
+ file_name_start++; // Move past '/', is valid as file_name_end is valid.
+ size_t file_name_len = file_name_end - file_name_start;
+ 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());
+ return true;
+}
+
+/*
+ * Computes the odex file for the given apk_path and instruction_set.
+ * /system/framework/whatever.jar -> /system/framework/oat/<isa>/whatever.odex
+ *
+ * Returns false if it failed to determine the odex file path.
+ */
+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);
+ return false;
+ }
+ std::string path_component(apk_path, path_end - apk_path);
+
+ const char *name_begin = path_end + 1;
+ const char *extension_start = strrchr(name_begin, '.');
+ if (extension_start == nullptr) {
+ ALOGE("apk_path '%s' has no extension.\n", apk_path);
+ return false;
+ }
+ std::string name_component(name_begin, extension_start - name_begin);
+
+ std::string new_path = StringPrintf("%s/oat/%s/%s.odex.b",
+ path_component.c_str(),
+ instruction_set,
+ name_component.c_str());
+ CHECK_LT(new_path.length(), PKG_PATH_MAX);
+ strcpy(path, new_path.c_str());
+ return true;
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX],
+ const char *src,
+ const char *instruction_set) {
+ size_t srclen = strlen(src);
+
+ /* demand that we are an absolute path */
+ if ((src == 0) || (src[0] != '/') || strstr(src,"..")) {
+ return false;
+ }
+
+ if (srclen > PKG_PATH_MAX) { // XXX: PKG_NAME_MAX?
+ return false;
+ }
+
+ std::string from_src = std::string(src + 1);
+ std::replace(from_src.begin(), from_src.end(), '/', '@');
+
+ std::string assembled_path = StringPrintf("%s/%s/%s/%s%s",
+ OTAPreoptService::kOTADataDirectory,
+ DALVIK_CACHE,
+ instruction_set,
+ from_src.c_str(),
+ DALVIK_CACHE_POSTFIX2);
+
+ if (assembled_path.length() + 1 > PKG_PATH_MAX) {
+ return false;
+ }
+ strcpy(path, assembled_path.c_str());
+
+ 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;
+
+ switch (type) {
+ case SELINUX_WARNING:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case SELINUX_INFO:
+ priority = ANDROID_LOG_INFO;
+ break;
+ default:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ }
+ va_start(ap, fmt);
+ LOG_PRI_VA(priority, "SELinux", fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+static int otapreopt_main(const int argc, char *argv[]) {
+ int selinux_enabled = (is_selinux_enabled() > 0);
+
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(argv);
+
+ ALOGI("otapreopt firing up\n");
+
+ if (argc < 2) {
+ ALOGE("Expecting parameters");
+ exit(1);
+ }
+
+ union selinux_callback cb;
+ 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);
+ }
+
+ int ret = android::installd::gOps.Main(argc, argv);
+
+ return ret;
+}
+
+} // namespace installd
+} // namespace android
+
+int main(const int argc, char *argv[]) {
+ return android::installd::otapreopt_main(argc, argv);
+}
diff --git a/cmds/installd/string_helpers.h b/cmds/installd/string_helpers.h
new file mode 100644
index 0000000..e8fcdef
--- /dev/null
+++ b/cmds/installd/string_helpers.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 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 ART_OTAPREOPT_STRING_HELPERS_H_
+#define ART_OTAPREOPT_STRING_HELPERS_H_
+
+#include <sstream>
+#include <string>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace installd {
+
+static inline bool StringStartsWith(const std::string& target,
+ const char* prefix) {
+ return target.compare(0, strlen(prefix), prefix) == 0;
+}
+
+// Split the input according to the separator character. Doesn't honor quotation.
+static inline std::vector<std::string> Split(const std::string& in, const char separator) {
+ if (in.empty()) {
+ return std::vector<std::string>();
+ }
+
+ std::vector<std::string> ret;
+ std::stringstream strstr(in);
+ std::string token;
+
+ while (std::getline(strstr, token, separator)) {
+ ret.push_back(token);
+ }
+
+ return ret;
+}
+
+template <typename StringT>
+static inline std::string Join(const std::vector<StringT>& strings, char separator) {
+ if (strings.empty()) {
+ return "";
+ }
+
+ std::string result(strings[0]);
+ for (size_t i = 1; i < strings.size(); ++i) {
+ result += separator;
+ result += strings[i];
+ }
+ return result;
+}
+
+} // namespace installd
+} // namespace android
+
+#endif // ART_OTAPREOPT_STRING_HELPERS_H_
diff --git a/cmds/installd/system_properties.h b/cmds/installd/system_properties.h
new file mode 100644
index 0000000..1b5fb3a
--- /dev/null
+++ b/cmds/installd/system_properties.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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_SYSTEM_PROPERTIES_H_
+#define OTAPREOPT_SYSTEM_PROPERTIES_H_
+
+#include <fstream>
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace installd {
+
+// Helper class to read system properties into and manage as a string->string map.
+class SystemProperties {
+ public:
+ bool Load(const std::string& strFile) {
+ std::ifstream input_stream(strFile);
+
+ if (!input_stream.is_open()) {
+ return false;
+ }
+
+ while (!input_stream.eof()) {
+ // Read the next line.
+ std::string line;
+ getline(input_stream, line);
+
+ // Is the line empty? Simplifies the next check.
+ if (line.empty()) {
+ continue;
+ }
+
+ // Is this a comment (starts with pound)?
+ if (line[0] == '#') {
+ continue;
+ }
+
+ size_t equals_pos = line.find('=');
+ if (equals_pos == std::string::npos || equals_pos == 0) {
+ // Did not find equals sign, or it's the first character - isn't a valid line.
+ continue;
+ }
+
+ std::string key = line.substr(0, equals_pos);
+ std::string value = line.substr(equals_pos + 1,
+ line.length() - equals_pos + 1);
+
+ properties_.insert(std::make_pair(key, value));
+ }
+
+ return true;
+ }
+
+ // Look up the key in the map. Returns null if the key isn't mapped.
+ const std::string* GetProperty(const std::string& key) const {
+ auto it = properties_.find(key);
+ if (it != properties_.end()) {
+ return &it->second;
+ }
+ return nullptr;
+ }
+
+ void SetProperty(const std::string& key, const std::string& value) {
+ properties_.insert(std::make_pair(key, value));
+ }
+
+ private:
+ // The actual map.
+ std::unordered_map<std::string, std::string> properties_;
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // OTAPREOPT_SYSTEM_PROPERTIES_H_
diff --git a/data/etc/android.hardware.vr.high_performance.xml b/data/etc/android.hardware.vr.high_performance.xml
new file mode 100644
index 0000000..776f4f7
--- /dev/null
+++ b/data/etc/android.hardware.vr.high_performance.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 is the set of features required for a VR-compatible device -->
+<permissions>
+ <feature name="android.software.vr.mode" />
+ <feature name="android.hardware.vr.high_performance" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 5edf0e8..9cb4d6d 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -52,6 +52,9 @@
<!-- Feature to specify if the device supports a VR mode. -->
<feature name="android.software.vr.mode" />
+ <!-- Devices with all optimizations required to be a "VR Ready" device that
+ pass all CTS tests for this feature must include feature
+ android.hardware.vr.high_performance -->
<!-- devices with GPS must include android.hardware.location.gps.xml -->
<!-- devices with an autofocus camera and/or flash must include either
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 0abf8f3..5956e13 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_PARCEL_H
#define ANDROID_PARCEL_H
+#include <string>
#include <vector>
#include <cutils/native_handle.h>
@@ -119,6 +120,10 @@
status_t writeChar(char16_t val);
status_t writeByte(int8_t val);
+ // Take a UTF8 encoded string, convert to UTF16, write it to the parcel.
+ status_t writeUtf8AsUtf16(const std::string& str);
+ status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str);
+
status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val);
status_t writeByteVector(const std::vector<int8_t>& val);
status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val);
@@ -136,6 +141,9 @@
status_t writeString16Vector(
const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val);
status_t writeString16Vector(const std::vector<String16>& val);
+ status_t writeUtf8VectorAsUtf16Vector(
+ const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val);
+ status_t writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val);
status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val);
status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
@@ -230,6 +238,10 @@
int8_t readByte() const;
status_t readByte(int8_t *pArg) const;
+ // Read a UTF16 encoded string, convert to UTF8
+ status_t readUtf8FromUtf16(std::string* str) const;
+ status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const;
+
const char* readCString() const;
String8 readString8() const;
String16 readString16() const;
@@ -274,6 +286,9 @@
status_t readString16Vector(
std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const;
status_t readString16Vector(std::vector<String16>* val) const;
+ status_t readUtf8VectorFromUtf16Vector(
+ std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const;
+ status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
template<typename T>
status_t read(Flattenable<T>& val) const;
diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
index 370f5d5..a515f39 100644
--- a/include/gui/BufferItem.h
+++ b/include/gui/BufferItem.h
@@ -125,6 +125,10 @@
// Indicates that this buffer was queued by the producer. When in single
// buffer mode acquire() can return a BufferItem that wasn't in the queue.
bool mQueuedBuffer;
+
+ // Indicates that this BufferItem contains a stale buffer which has already
+ // been released by the BufferQueue.
+ bool mIsStale;
};
} // namespace android
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
index fbd5114..e2e73a0 100644
--- a/include/gui/BufferQueueCore.h
+++ b/include/gui/BufferQueueCore.h
@@ -105,24 +105,32 @@
// connected, mDequeueCondition must be broadcast.
int getMaxBufferCountLocked() const;
- // freeBufferLocked frees the GraphicBuffer and sync resources for the
+ // This performs the same computation but uses the given arguments instead
+ // of the member variables for mMaxBufferCount, mAsyncMode, and
+ // mDequeueBufferCannotBlock.
+ int getMaxBufferCountLocked(bool asyncMode,
+ bool dequeueBufferCannotBlock, int maxBufferCount) const;
+
+ // clearBufferSlotLocked frees the GraphicBuffer and sync resources for the
// given slot.
- void freeBufferLocked(int slot, bool validate = true);
+ void clearBufferSlotLocked(int slot);
// freeAllBuffersLocked frees the GraphicBuffer and sync resources for
// all slots, even if they're currently dequeued, queued, or acquired.
void freeAllBuffersLocked();
- // stillTracking returns true iff the buffer item is still being tracked
- // in one of the slots.
- bool stillTracking(const BufferItem* item) const;
+ // If delta is positive, makes more slots available. If negative, takes
+ // away slots. Returns false if the request can't be met.
+ bool adjustAvailableSlotsLocked(int delta);
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
void waitWhileAllocatingLocked() const;
+#if DEBUG_ONLY_CODE
// validateConsistencyLocked ensures that the free lists are in sync with
// the information stored in mSlots
void validateConsistencyLocked() const;
+#endif
// mAllocator is the connection to SurfaceFlinger that is used to allocate
// new GraphicBuffer objects.
@@ -179,13 +187,20 @@
Fifo mQueue;
// mFreeSlots contains all of the slots which are FREE and do not currently
- // have a buffer attached
+ // have a buffer attached.
std::set<int> mFreeSlots;
// mFreeBuffers contains all of the slots which are FREE and currently have
- // a buffer attached
+ // a buffer attached.
std::list<int> mFreeBuffers;
+ // mUnusedSlots contains all slots that are currently unused. They should be
+ // free and not have a buffer attached.
+ std::list<int> mUnusedSlots;
+
+ // mActiveBuffers contains all slots which have a non-FREE buffer attached.
+ std::set<int> mActiveBuffers;
+
// mDequeueCondition is a condition variable used for dequeueBuffer in
// synchronous mode.
mutable Condition mDequeueCondition;
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
index 645a07b..dc05e98 100644
--- a/include/gui/BufferQueueProducer.h
+++ b/include/gui/BufferQueueProducer.h
@@ -187,9 +187,9 @@
// BufferQueueCore::INVALID_BUFFER_SLOT otherwise
int getFreeBufferLocked() const;
- // Returns the next free slot if one less than or equal to maxBufferCount
- // is available or BufferQueueCore::INVALID_BUFFER_SLOT otherwise
- int getFreeSlotLocked(int maxBufferCount) const;
+ // Returns the next free slot if one is available or
+ // BufferQueueCore::INVALID_BUFFER_SLOT otherwise
+ int getFreeSlotLocked() const;
// waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
// block if there are no available slots and we are not in non-blocking
@@ -200,8 +200,7 @@
Dequeue,
Attach,
};
- status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found,
- status_t* returnFlags) const;
+ status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const;
sp<BufferQueueCore> mCore;
diff --git a/include/gui/BufferSlot.h b/include/gui/BufferSlot.h
index 17a654a..943fa82 100644
--- a/include/gui/BufferSlot.h
+++ b/include/gui/BufferSlot.h
@@ -174,14 +174,15 @@
struct BufferSlot {
BufferSlot()
- : mEglDisplay(EGL_NO_DISPLAY),
+ : mGraphicBuffer(nullptr),
+ mEglDisplay(EGL_NO_DISPLAY),
mBufferState(),
mRequestBufferCalled(false),
mFrameNumber(0),
mEglFence(EGL_NO_SYNC_KHR),
+ mFence(Fence::NO_FENCE),
mAcquireCalled(false),
- mNeedsCleanupOnRelease(false),
- mAttachedByConsumer(false) {
+ mNeedsReallocation(false) {
}
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
@@ -191,8 +192,6 @@
// mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects.
EGLDisplay mEglDisplay;
- static const char* bufferStateName(BufferState state);
-
// mBufferState is the current state of this buffer slot.
BufferState mBufferState;
@@ -227,15 +226,10 @@
// Indicates whether this buffer has been seen by a consumer yet
bool mAcquireCalled;
- // Indicates whether this buffer needs to be cleaned up by the
- // consumer. This is set when a buffer in ACQUIRED state is freed.
- // It causes releaseBuffer to return STALE_BUFFER_SLOT.
- bool mNeedsCleanupOnRelease;
-
- // Indicates whether the buffer was attached on the consumer side.
- // If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued
- // to prevent the producer from using a stale cached buffer.
- bool mAttachedByConsumer;
+ // Indicates whether the buffer was re-allocated without notifying the
+ // producer. If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when
+ // dequeued to prevent the producer from using a stale cached buffer.
+ bool mNeedsReallocation;
};
} // namespace android
diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h
index d4c9ee5..ebce7fb 100644
--- a/include/gui/IGraphicBufferConsumer.h
+++ b/include/gui/IGraphicBufferConsumer.h
@@ -199,7 +199,9 @@
// cannot be less than maxAcquiredBufferCount.
//
// Return of a value other than NO_ERROR means an error has occurred:
- // * BAD_VALUE - bufferCount was out of range (see above).
+ // * BAD_VALUE - one of the below conditions occurred:
+ // * bufferCount was out of range (see above).
+ // * failure to adjust the number of available slots.
// * INVALID_OPERATION - attempting to call this after a producer connected.
virtual status_t setMaxBufferCount(int bufferCount) = 0;
@@ -212,7 +214,9 @@
// to be exceeded.
//
// Return of a value other than NO_ERROR means an error has occurred:
- // * BAD_VALUE - maxAcquiredBuffers was out of range (see above).
+ // * BAD_VALUE - one of the below conditions occurred:
+ // * maxAcquiredBuffers was out of range (see above).
+ // * failure to adjust the number of available slots.
// * INVALID_OPERATION - attempting to call this after a producer connected.
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h
index 8646981..1f4c8ac 100644
--- a/include/gui/IGraphicBufferProducer.h
+++ b/include/gui/IGraphicBufferProducer.h
@@ -101,8 +101,9 @@
// * NO_INIT - the buffer queue has been abandoned.
// * BAD_VALUE - one of the below conditions occurred:
// * bufferCount was out of range (see above)
- // * client has one or more buffers dequeued
+ // * client has too many buffers dequeued
// * this call would cause the maxBufferCount value to be exceeded
+ // * failure to adjust the number of available slots.
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) = 0;
// Set the async flag if the producer intends to asynchronously queue
@@ -115,8 +116,10 @@
//
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned.
- // * BAD_VALUE - this call would cause the maxBufferCount value to be
+ // * BAD_VALUE - one of the following has occurred:
+ // * this call would cause the maxBufferCount value to be
// exceeded
+ // * failure to adjust the number of available slots.
virtual status_t setAsyncMode(bool async) = 0;
// dequeueBuffer requests a new buffer slot for the client to use. Ownership
@@ -436,6 +439,9 @@
// * the producer is already connected
// * api was out of range (see above).
// * output was NULL.
+ // * Failure to adjust the number of available slots. This can
+ // happen because of trying to allocate/deallocate the async
+ // buffer in response to the value of producerControlledByApp.
// * DEAD_OBJECT - the token is hosted by an already-dead process
//
// Additional negative errors may be returned by the internals, they
@@ -534,6 +540,11 @@
// timeout of -1. If set (to a value other than -1), this will disable
// non-blocking mode and its corresponding spare buffer (which is used to
// ensure a buffer is always available).
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * BAD_VALUE - Failure to adjust the number of available slots. This can
+ // happen because of trying to allocate/deallocate the async
+ // buffer.
virtual status_t setDequeueTimeout(nsecs_t timeout) = 0;
};
diff --git a/include/media/openmax/OMX_AudioExt.h b/include/media/openmax/OMX_AudioExt.h
index 2a1c3f2..05c2232 100644
--- a/include/media/openmax/OMX_AudioExt.h
+++ b/include/media/openmax/OMX_AudioExt.h
@@ -94,6 +94,15 @@
OMX_S32 nPCMLimiterEnable; /**< Signal level limiting, 0 for disable, 1 for enable, -1 if unspecified */
} OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE;
+typedef struct OMX_AUDIO_PARAM_ANDROID_PROFILETYPE {
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_U32 eProfile; /**< type is OMX_AUDIO_AACPROFILETYPE or OMX_AUDIO_WMAPROFILETYPE
+ depending on context */
+ OMX_U32 nProfileIndex; /**< Used to query for individual profile support information */
+} OMX_AUDIO_PARAM_ANDROID_PROFILETYPE;
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
index 25bea1f..8bfc49d 100644
--- a/include/media/openmax/OMX_IndexExt.h
+++ b/include/media/openmax/OMX_IndexExt.h
@@ -61,6 +61,7 @@
OMX_IndexParamAudioAndroidOpus, /**< reference: OMX_AUDIO_PARAM_ANDROID_OPUSTYPE */
OMX_IndexParamAudioAndroidAacPresentation, /**< reference: OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE */
OMX_IndexParamAudioAndroidEac3, /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */
+ OMX_IndexParamAudioProfileQuerySupported, /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */
/* Image parameters and configurations */
OMX_IndexExtImageStartUnused = OMX_IndexKhronosExtensions + 0x00500000,
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index a237684..1f6bda2 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -287,12 +287,18 @@
return new IPCThreadState;
}
- if (gShutdown) return NULL;
+ if (gShutdown) {
+ ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");
+ return NULL;
+ }
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) {
- if (pthread_key_create(&gTLS, threadDestructor) != 0) {
+ int key_create_value = pthread_key_create(&gTLS, threadDestructor);
+ if (key_create_value != 0) {
pthread_mutex_unlock(&gTLSMutex);
+ ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",
+ strerror(key_create_value));
return NULL;
}
gHaveTLS = true;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 6893e39..d3fe158 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -749,6 +749,37 @@
return NULL;
}
+status_t Parcel::writeUtf8AsUtf16(const std::string& str) {
+ const uint8_t* strData = (uint8_t*)str.data();
+ const size_t strLen= str.length();
+ const ssize_t utf16Len = utf8_to_utf16_length(strData, strLen);
+ if (utf16Len < 0 || utf16Len> std::numeric_limits<int32_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ status_t err = writeInt32(utf16Len);
+ if (err) {
+ return err;
+ }
+
+ // Allocate enough bytes to hold our converted string and its terminating NULL.
+ void* dst = writeInplace((utf16Len + 1) * sizeof(char16_t));
+ if (!dst) {
+ return NO_MEMORY;
+ }
+
+ utf8_to_utf16(strData, strLen, (char16_t*)dst);
+
+ return NO_ERROR;
+}
+
+status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) {
+ if (!str) {
+ return writeInt32(-1);
+ }
+ return writeUtf8AsUtf16(*str);
+}
+
status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val)
{
if (!val) {
@@ -852,6 +883,15 @@
return writeNullableTypedVector(val, &Parcel::writeString16);
}
+status_t Parcel::writeUtf8VectorAsUtf16Vector(
+ const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) {
+ return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16);
+}
+
+status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val) {
+ return writeTypedVector(val, &Parcel::writeUtf8AsUtf16);
+}
+
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
@@ -1491,6 +1531,14 @@
return readTypedVector(val, &Parcel::readString16);
}
+status_t Parcel::readUtf8VectorFromUtf16Vector(
+ std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const {
+ return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16);
+}
+
+status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const {
+ return readTypedVector(val, &Parcel::readUtf8FromUtf16);
+}
status_t Parcel::readInt32(int32_t *pArg) const
{
@@ -1649,6 +1697,46 @@
return int8_t(readInt32());
}
+status_t Parcel::readUtf8FromUtf16(std::string* str) const {
+ size_t utf16Size = 0;
+ const char16_t* src = readString16Inplace(&utf16Size);
+ if (!src) {
+ return UNEXPECTED_NULL;
+ }
+
+ // Save ourselves the trouble, we're done.
+ if (utf16Size == 0u) {
+ str->clear();
+ return NO_ERROR;
+ }
+
+ ssize_t utf8Size = utf16_to_utf8_length(src, utf16Size);
+ if (utf8Size < 0) {
+ 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]));
+ str->resize(utf8Size);
+ return NO_ERROR;
+}
+
+status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const {
+ const int32_t start = dataPosition();
+ int32_t size;
+ status_t status = readInt32(&size);
+ str->reset();
+
+ if (status != OK || size < 0) {
+ return status;
+ }
+
+ setDataPosition(start);
+ str->reset(new std::string());
+ return readUtf8FromUtf16(str->get());
+}
+
const char* Parcel::readCString() const
{
const size_t avail = mDataSize-mDataPos;
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 8a965dd..635020e 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -36,6 +36,8 @@
# Don't warn about struct padding
LOCAL_CPPFLAGS += -Wno-padded
+LOCAL_CPPFLAGS += -DDEBUG_ONLY_CODE=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
+
LOCAL_SRC_FILES := \
IGraphicBufferConsumer.cpp \
IConsumerListener.cpp \
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
index de8ff70..036ef1e 100644
--- a/libs/gui/BufferItem.cpp
+++ b/libs/gui/BufferItem.cpp
@@ -39,7 +39,8 @@
mTransformToDisplayInverse(false),
mSurfaceDamage(),
mSingleBufferMode(false),
- mQueuedBuffer(true) {
+ mQueuedBuffer(true),
+ mIsStale(false) {
}
BufferItem::~BufferItem() {}
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 6f9f21f..9c1bbe4 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -20,6 +20,12 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
+#if DEBUG_ONLY_CODE
+#define VALIDATE_CONSISTENCY() do { mCore->validateConsistencyLocked(); } while (0)
+#else
+#define VALIDATE_CONSISTENCY()
+#endif
+
#include <gui/BufferItem.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
@@ -49,7 +55,7 @@
// buffer so that the consumer can successfully set up the newly acquired
// buffer before releasing the old one.
int numAcquiredBuffers = 0;
- for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isAcquired()) {
++numAcquiredBuffers;
}
@@ -133,7 +139,8 @@
BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
" size=%zu",
desiredPresent, expectedPresent, mCore->mQueue.size());
- if (mCore->stillTracking(front)) {
+
+ if (!front->mIsStale) {
// Front buffer is still in mSlots, so mark the slot as free
mSlots[front->mSlot].mBufferState.freeQueued();
@@ -144,13 +151,17 @@
mSlots[front->mSlot].mBufferState.isFree()) {
mSlots[front->mSlot].mBufferState.mShared = false;
}
- // Don't put the shared buffer on the free list.
+
+ // Don't put the shared buffer on the free list
if (!mSlots[front->mSlot].mBufferState.isShared()) {
+ mCore->mActiveBuffers.erase(front->mSlot);
mCore->mFreeBuffers.push_back(front->mSlot);
}
+
listener = mCore->mConnectedProducerListener;
++numDroppedBuffers;
}
+
mCore->mQueue.erase(front);
front = mCore->mQueue.begin();
}
@@ -205,6 +216,7 @@
outBuffer->mSurfaceDamage = Region::INVALID_REGION;
outBuffer->mSingleBufferMode = true;
outBuffer->mQueuedBuffer = false;
+ outBuffer->mIsStale = false;
} else {
slot = front->mSlot;
*outBuffer = *front;
@@ -216,10 +228,9 @@
BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle);
- // If the front buffer is still being tracked, update its slot state
- if (mCore->stillTracking(outBuffer)) {
+
+ if (!outBuffer->mIsStale) {
mSlots[slot].mAcquireCalled = true;
- mSlots[slot].mNeedsCleanupOnRelease = false;
// Don't decrease the queue count if the BufferItem wasn't
// previously in the queue. This happens in single buffer mode when
// the queue is empty and the BufferItem is created above.
@@ -247,7 +258,7 @@
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
- mCore->validateConsistencyLocked();
+ VALIDATE_CONSISTENCY();
}
if (listener != NULL) {
@@ -270,7 +281,7 @@
return NO_INIT;
}
- if (mCore->mSingleBufferMode) {
+ if (mCore->mSingleBufferMode || slot == mCore->mSingleBufferSlot) {
BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
"mode");
return BAD_VALUE;
@@ -287,9 +298,11 @@
}
mSlots[slot].mBufferState.detachConsumer();
- mCore->freeBufferLocked(slot);
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeSlots.insert(slot);
+ mCore->clearBufferSlotLocked(slot);
mCore->mDequeueCondition.broadcast();
- mCore->validateConsistencyLocked();
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
@@ -316,7 +329,7 @@
// Make sure we don't have too many acquired buffers
int numAcquiredBuffers = 0;
- for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isAcquired()) {
++numAcquiredBuffers;
}
@@ -351,14 +364,14 @@
return NO_MEMORY;
}
+ mCore->mActiveBuffers.insert(found);
*outSlot = found;
ATRACE_BUFFER_INDEX(*outSlot);
BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
mSlots[*outSlot].mGraphicBuffer = buffer;
mSlots[*outSlot].mBufferState.attachConsumer();
- mSlots[*outSlot].mAttachedByConsumer = true;
- mSlots[*outSlot].mNeedsCleanupOnRelease = false;
+ mSlots[*outSlot].mNeedsReallocation = true;
mSlots[*outSlot].mFence = Fence::NO_FENCE;
mSlots[*outSlot].mFrameNumber = 0;
@@ -379,7 +392,7 @@
// for attached buffers.
mSlots[*outSlot].mAcquireCalled = false;
- mCore->validateConsistencyLocked();
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
@@ -411,41 +424,35 @@
return STALE_BUFFER_SLOT;
}
-
- if (mSlots[slot].mBufferState.isAcquired()) {
- mSlots[slot].mEglDisplay = eglDisplay;
- mSlots[slot].mEglFence = eglFence;
- mSlots[slot].mFence = releaseFence;
- mSlots[slot].mBufferState.release();
-
- // After leaving single buffer mode, the shared buffer will
- // still be around. Mark it as no longer shared if this
- // operation causes it to be free.
- if (!mCore->mSingleBufferMode &&
- mSlots[slot].mBufferState.isFree()) {
- mSlots[slot].mBufferState.mShared = false;
- }
- // Don't put the shared buffer on the free list.
- if (!mSlots[slot].mBufferState.isShared()) {
- mCore->mFreeBuffers.push_back(slot);
- }
-
- listener = mCore->mConnectedProducerListener;
- BQ_LOGV("releaseBuffer: releasing slot %d", slot);
- } else if (mSlots[slot].mNeedsCleanupOnRelease) {
- BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
- "(state = %s)", slot, mSlots[slot].mBufferState.string());
- mSlots[slot].mNeedsCleanupOnRelease = false;
- return STALE_BUFFER_SLOT;
- } else {
+ if (!mSlots[slot].mBufferState.isAcquired()) {
BQ_LOGE("releaseBuffer: attempted to release buffer slot %d "
"but its state was %s", slot,
mSlots[slot].mBufferState.string());
return BAD_VALUE;
}
+ mSlots[slot].mEglDisplay = eglDisplay;
+ mSlots[slot].mEglFence = eglFence;
+ mSlots[slot].mFence = releaseFence;
+ mSlots[slot].mBufferState.release();
+
+ // After leaving single buffer mode, the shared buffer will
+ // still be around. Mark it as no longer shared if this
+ // operation causes it to be free.
+ if (!mCore->mSingleBufferMode && mSlots[slot].mBufferState.isFree()) {
+ mSlots[slot].mBufferState.mShared = false;
+ }
+ // Don't put the shared buffer on the free list.
+ if (!mSlots[slot].mBufferState.isShared()) {
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeBuffers.push_back(slot);
+ }
+
+ listener = mCore->mConnectedProducerListener;
+ BQ_LOGV("releaseBuffer: releasing slot %d", slot);
+
mCore->mDequeueCondition.broadcast();
- mCore->validateConsistencyLocked();
+ VALIDATE_CONSISTENCY();
} // Autolock scope
// Call back without lock held
@@ -497,6 +504,7 @@
mCore->mConsumerListener = NULL;
mCore->mQueue.clear();
mCore->freeAllBuffersLocked();
+ mCore->mSingleBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
mCore->mDequeueCondition.broadcast();
return NO_ERROR;
}
@@ -579,6 +587,15 @@
return BAD_VALUE;
}
+ int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+ mCore->mDequeueBufferCannotBlock, bufferCount) -
+ mCore->getMaxBufferCountLocked();
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
+ BQ_LOGE("setMaxBufferCount: BufferQueue failed to adjust the number of "
+ "available slots. Delta = %d", delta);
+ return BAD_VALUE;
+ }
+
mCore->mMaxBufferCount = bufferCount;
return NO_ERROR;
}
@@ -612,8 +629,17 @@
return BAD_VALUE;
}
+ if (!mCore->adjustAvailableSlotsLocked(
+ maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount)) {
+ BQ_LOGE("setMaxAcquiredBufferCount: BufferQueue failed to adjust the "
+ "number of available slots. Delta = %d",
+ maxAcquiredBuffers - mCore->mMaxAcquiredBufferCount);
+ return BAD_VALUE;
+ }
+
BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index c24ad19..5c80694 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -20,6 +20,12 @@
#define EGL_EGLEXT_PROTOTYPES
+#if DEBUG_ONLY_CODE
+#define VALIDATE_CONSISTENCY() do { validateConsistencyLocked(); } while (0)
+#else
+#define VALIDATE_CONSISTENCY()
+#endif
+
#include <inttypes.h>
#include <gui/BufferItem.h>
@@ -52,6 +58,8 @@
mQueue(),
mFreeSlots(),
mFreeBuffers(),
+ mUnusedSlots(),
+ mActiveBuffers(),
mDequeueCondition(),
mDequeueBufferCannotBlock(false),
mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
@@ -82,8 +90,14 @@
BQ_LOGE("createGraphicBufferAlloc failed");
}
}
- for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
- mFreeSlots.insert(slot);
+
+ int numStartingBuffers = getMaxBufferCountLocked();
+ for (int s = 0; s < numStartingBuffers; s++) {
+ mFreeSlots.insert(s);
+ }
+ for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;
+ s++) {
+ mUnusedSlots.push_front(s);
}
}
@@ -113,32 +127,26 @@
mDefaultHeight, mDefaultBufferFormat, mTransformHint, mQueue.size(),
fifo.string());
- // Trim the free buffers so as to not spam the dump
- int maxBufferCount = 0;
- for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
- const BufferSlot& slot(mSlots[s]);
- if (!slot.mBufferState.isFree() ||
- slot.mGraphicBuffer != NULL) {
- maxBufferCount = s + 1;
- break;
- }
+ for (int s : mActiveBuffers) {
+ const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+ result.appendFormat("%s%s[%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+ prefix, (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
+ buffer.get(), mSlots[s].mBufferState.string(), buffer->handle,
+ buffer->width, buffer->height, buffer->stride, buffer->format);
+
+ }
+ for (int s : mFreeBuffers) {
+ const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+ result.appendFormat("%s [%02d:%p] state=%-8s, %p [%4ux%4u:%4u,%3X]\n",
+ prefix, s, buffer.get(), mSlots[s].mBufferState.string(),
+ buffer->handle, buffer->width, buffer->height, buffer->stride,
+ buffer->format);
}
- for (int s = 0; s < maxBufferCount; ++s) {
- const BufferSlot& slot(mSlots[s]);
- const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
- result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
- (slot.mBufferState.isAcquired()) ? ">" : " ",
- s, buffer.get(),
- slot.mBufferState.string());
-
- if (buffer != NULL) {
- result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
- buffer->width, buffer->height, buffer->stride,
- buffer->format);
- }
-
- result.append("\n");
+ for (int s : mFreeSlots) {
+ const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
+ result.appendFormat("%s [%02d:%p] state=%-8s\n", prefix, s,
+ buffer.get(), mSlots[s].mBufferState.string());
}
}
@@ -156,44 +164,33 @@
return getMinUndequeuedBufferCountLocked() + 1;
}
+int BufferQueueCore::getMaxBufferCountLocked(bool asyncMode,
+ bool dequeueBufferCannotBlock, int maxBufferCount) const {
+ int maxCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount +
+ ((asyncMode || dequeueBufferCannotBlock) ? 1 : 0);
+ maxCount = std::min(maxBufferCount, maxCount);
+ return maxCount;
+}
+
int BufferQueueCore::getMaxBufferCountLocked() const {
int maxBufferCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount +
- (mAsyncMode || mDequeueBufferCannotBlock ? 1 : 0);
+ ((mAsyncMode || mDequeueBufferCannotBlock) ? 1 : 0);
// limit maxBufferCount by mMaxBufferCount always
maxBufferCount = std::min(mMaxBufferCount, maxBufferCount);
- // Any buffers that are dequeued by the producer or sitting in the queue
- // waiting to be consumed need to have their slots preserved. Such buffers
- // will temporarily keep the max buffer count up until the slots no longer
- // need to be preserved.
- for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- BufferState state = mSlots[s].mBufferState;
- if (state.isQueued() || state.isDequeued()) {
- maxBufferCount = s + 1;
- }
- }
-
return maxBufferCount;
}
-void BufferQueueCore::freeBufferLocked(int slot, bool validate) {
- BQ_LOGV("freeBufferLocked: slot %d", slot);
- bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL;
+void BufferQueueCore::clearBufferSlotLocked(int slot) {
+ BQ_LOGV("clearBufferSlotLocked: slot %d", slot);
+
mSlots[slot].mGraphicBuffer.clear();
- if (mSlots[slot].mBufferState.isAcquired()) {
- mSlots[slot].mNeedsCleanupOnRelease = true;
- }
- if (!mSlots[slot].mBufferState.isFree()) {
- mFreeSlots.insert(slot);
- } else if (hadBuffer) {
- // If the slot was FREE, but we had a buffer, we need to move this slot
- // from the free buffers list to the the free slots list
- mFreeBuffers.remove(slot);
- mFreeSlots.insert(slot);
- }
- mSlots[slot].mAcquireCalled = false;
+ mSlots[slot].mBufferState.reset();
+ mSlots[slot].mRequestBufferCalled = false;
mSlots[slot].mFrameNumber = 0;
+ mSlots[slot].mAcquireCalled = false;
+ mSlots[slot].mNeedsReallocation = true;
// Destroy fence as BufferQueue now takes ownership
if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) {
@@ -201,35 +198,63 @@
mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
}
mSlots[slot].mFence = Fence::NO_FENCE;
- if (validate) {
- validateConsistencyLocked();
- }
+ mSlots[slot].mEglDisplay = EGL_NO_DISPLAY;
}
void BufferQueueCore::freeAllBuffersLocked() {
- mBufferHasBeenQueued = false;
- for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- freeBufferLocked(s, false);
- mSlots[s].mBufferState.reset();
+ for (int s : mFreeSlots) {
+ clearBufferSlotLocked(s);
}
- mSingleBufferSlot = INVALID_BUFFER_SLOT;
- validateConsistencyLocked();
+
+ for (int s : mFreeBuffers) {
+ mFreeSlots.insert(s);
+ clearBufferSlotLocked(s);
+ }
+ mFreeBuffers.clear();
+
+ for (int s : mActiveBuffers) {
+ mFreeSlots.insert(s);
+ clearBufferSlotLocked(s);
+ }
+ mActiveBuffers.clear();
+
+ for (auto& b : mQueue) {
+ b.mIsStale = true;
+ }
+
+ VALIDATE_CONSISTENCY();
}
-bool BufferQueueCore::stillTracking(const BufferItem* item) const {
- const BufferSlot& slot = mSlots[item->mSlot];
-
- BQ_LOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } "
- "slot { slot=%d/%" PRIu64 " buffer=%p }",
- item->mSlot, item->mFrameNumber,
- (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0),
- item->mSlot, slot.mFrameNumber,
- (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0));
-
- // Compare item with its original buffer slot. We can check the slot as
- // the buffer would not be moved to a different slot by the producer.
- return (slot.mGraphicBuffer != NULL) &&
- (item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
+bool BufferQueueCore::adjustAvailableSlotsLocked(int delta) {
+ if (delta >= 0) {
+ while (delta > 0) {
+ if (mUnusedSlots.empty()) {
+ return false;
+ }
+ int slot = mUnusedSlots.back();
+ mUnusedSlots.pop_back();
+ mFreeSlots.insert(slot);
+ delta--;
+ }
+ } else {
+ while (delta < 0) {
+ if (!mFreeSlots.empty()) {
+ auto slot = mFreeSlots.begin();
+ clearBufferSlotLocked(*slot);
+ mUnusedSlots.push_back(*slot);
+ mFreeSlots.erase(slot);
+ } else if (!mFreeBuffers.empty()) {
+ int slot = mFreeBuffers.back();
+ clearBufferSlotLocked(slot);
+ mUnusedSlots.push_back(slot);
+ mFreeBuffers.pop_back();
+ } else {
+ return false;
+ }
+ delta++;
+ }
+ }
+ return true;
}
void BufferQueueCore::waitWhileAllocatingLocked() const {
@@ -239,49 +264,131 @@
}
}
+#if DEBUG_ONLY_CODE
void BufferQueueCore::validateConsistencyLocked() const {
static const useconds_t PAUSE_TIME = 0;
+ int allocatedSlots = 0;
for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
bool isInFreeSlots = mFreeSlots.count(slot) != 0;
bool isInFreeBuffers =
std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
mFreeBuffers.cend();
- if (mSlots[slot].mBufferState.isFree() &&
- !mSlots[slot].mBufferState.isShared()) {
- if (mSlots[slot].mGraphicBuffer == NULL) {
- if (!isInFreeSlots) {
- BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot);
- usleep(PAUSE_TIME);
- }
- if (isInFreeBuffers) {
- BQ_LOGE("Slot %d is in mFreeSlots "
- "but is also in mFreeBuffers", slot);
- usleep(PAUSE_TIME);
- }
- } else {
- if (!isInFreeBuffers) {
- BQ_LOGE("Slot %d is FREE but is not in mFreeBuffers", slot);
- usleep(PAUSE_TIME);
- }
- if (isInFreeSlots) {
- BQ_LOGE("Slot %d is in mFreeBuffers "
- "but is also in mFreeSlots", slot);
- usleep(PAUSE_TIME);
- }
- }
- } else {
+ bool isInActiveBuffers = mActiveBuffers.count(slot) != 0;
+ bool isInUnusedSlots =
+ std::find(mUnusedSlots.cbegin(), mUnusedSlots.cend(), slot) !=
+ mUnusedSlots.cend();
+
+ if (isInFreeSlots || isInFreeBuffers || isInActiveBuffers) {
+ allocatedSlots++;
+ }
+
+ if (isInUnusedSlots) {
if (isInFreeSlots) {
- BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%s)",
- slot, mSlots[slot].mBufferState.string());
+ BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeSlots", slot);
usleep(PAUSE_TIME);
}
if (isInFreeBuffers) {
- BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%s)",
- slot, mSlots[slot].mBufferState.string());
+ BQ_LOGE("Slot %d is in mUnusedSlots and in mFreeBuffers", slot);
usleep(PAUSE_TIME);
}
+ if (isInActiveBuffers) {
+ BQ_LOGE("Slot %d is in mUnusedSlots and in mActiveBuffers",
+ slot);
+ usleep(PAUSE_TIME);
+ }
+ if (!mSlots[slot].mBufferState.isFree()) {
+ BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (mSlots[slot].mGraphicBuffer != NULL) {
+ BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer",
+ slot);
+ usleep(PAUSE_TIME);
+ }
+ } else if (isInFreeSlots) {
+ if (isInUnusedSlots) {
+ BQ_LOGE("Slot %d is in mFreeSlots and in mUnusedSlots", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (isInFreeBuffers) {
+ BQ_LOGE("Slot %d is in mFreeSlots and in mFreeBuffers", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (isInActiveBuffers) {
+ BQ_LOGE("Slot %d is in mFreeSlots and in mActiveBuffers", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (!mSlots[slot].mBufferState.isFree()) {
+ BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (mSlots[slot].mGraphicBuffer != NULL) {
+ BQ_LOGE("Slot %d is in mFreeSlots but has a buffer",
+ slot);
+ usleep(PAUSE_TIME);
+ }
+ } else if (isInFreeBuffers) {
+ if (isInUnusedSlots) {
+ BQ_LOGE("Slot %d is in mFreeBuffers and in mUnusedSlots", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (isInFreeSlots) {
+ BQ_LOGE("Slot %d is in mFreeBuffers and in mFreeSlots", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (isInActiveBuffers) {
+ BQ_LOGE("Slot %d is in mFreeBuffers and in mActiveBuffers",
+ slot);
+ usleep(PAUSE_TIME);
+ }
+ if (!mSlots[slot].mBufferState.isFree()) {
+ BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (mSlots[slot].mGraphicBuffer == NULL) {
+ BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot);
+ usleep(PAUSE_TIME);
+ }
+ } else if (isInActiveBuffers) {
+ if (isInUnusedSlots) {
+ BQ_LOGE("Slot %d is in mActiveBuffers and in mUnusedSlots",
+ slot);
+ usleep(PAUSE_TIME);
+ }
+ if (isInFreeSlots) {
+ BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeSlots", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (isInFreeBuffers) {
+ BQ_LOGE("Slot %d is in mActiveBuffers and in mFreeBuffers",
+ slot);
+ usleep(PAUSE_TIME);
+ }
+ if (mSlots[slot].mBufferState.isFree() &&
+ !mSlots[slot].mBufferState.isShared()) {
+ BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot);
+ usleep(PAUSE_TIME);
+ }
+ if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) {
+ BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot);
+ usleep(PAUSE_TIME);
+ }
+ } else {
+ BQ_LOGE("Slot %d isn't in any of mUnusedSlots, mFreeSlots, "
+ "mFreeBuffers, or mActiveBuffers", slot);
+ usleep(PAUSE_TIME);
}
}
+
+ if (allocatedSlots != getMaxBufferCountLocked()) {
+ BQ_LOGE("Number of allocated slots is incorrect. Allocated = %d, "
+ "Should be %d (%zu free slots, %zu free buffers, "
+ "%zu activeBuffers, %zu unusedSlots)", allocatedSlots,
+ getMaxBufferCountLocked(), mFreeSlots.size(),
+ mFreeBuffers.size(), mActiveBuffers.size(),
+ mUnusedSlots.size());
+ }
}
+#endif
} // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 5b1aaa0..3c06899 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -20,6 +20,12 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
+#if DEBUG_ONLY_CODE
+#define VALIDATE_CONSISTENCY() do { mCore->validateConsistencyLocked(); } while (0)
+#else
+#define VALIDATE_CONSISTENCY()
+#endif
+
#define EGL_EGLEXT_PROTOTYPES
#include <gui/BufferItem.h>
@@ -96,7 +102,7 @@
}
// There must be no dequeued buffers when changing the buffer count.
- for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isDequeued()) {
BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer");
return BAD_VALUE;
@@ -132,8 +138,15 @@
// buffers and will release all of its buffer references. We don't
// clear the queue, however, so that currently queued buffers still
// get displayed.
- mCore->freeAllBuffersLocked();
+ if (!mCore->adjustAvailableSlotsLocked(
+ maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount)) {
+ BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue failed to adjust "
+ "the number of available slots. Delta = %d",
+ maxDequeuedBuffers - mCore->mMaxDequeuedBufferCount);
+ return BAD_VALUE;
+ }
mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers;
+ VALIDATE_CONSISTENCY();
mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener;
} // Autolock scope
@@ -172,7 +185,17 @@
return BAD_VALUE;
}
+ int delta = mCore->getMaxBufferCountLocked(async,
+ mCore->mDequeueBufferCannotBlock, mCore->mMaxBufferCount)
+ - mCore->getMaxBufferCountLocked();
+
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
+ BQ_LOGE("setAsyncMode: BufferQueue failed to adjust the number of "
+ "available slots. Delta = %d", delta);
+ return BAD_VALUE;
+ }
mCore->mAsyncMode = async;
+ VALIDATE_CONSISTENCY();
mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener;
} // Autolock scope
@@ -188,25 +211,22 @@
if (mCore->mFreeBuffers.empty()) {
return BufferQueueCore::INVALID_BUFFER_SLOT;
}
- auto slot = mCore->mFreeBuffers.front();
+ int slot = mCore->mFreeBuffers.front();
mCore->mFreeBuffers.pop_front();
return slot;
}
-int BufferQueueProducer::getFreeSlotLocked(int maxBufferCount) const {
+int BufferQueueProducer::getFreeSlotLocked() const {
if (mCore->mFreeSlots.empty()) {
return BufferQueueCore::INVALID_BUFFER_SLOT;
}
- auto slot = *(mCore->mFreeSlots.begin());
- if (slot < maxBufferCount) {
- mCore->mFreeSlots.erase(slot);
- return slot;
- }
- return BufferQueueCore::INVALID_BUFFER_SLOT;
+ auto slot = mCore->mFreeSlots.begin();
+ mCore->mFreeSlots.erase(slot);
+ return *slot;
}
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
- int* found, status_t* returnFlags) const {
+ int* found) const {
auto callerString = (caller == FreeSlotCaller::Dequeue) ?
"dequeueBuffer" : "attachBuffer";
bool tryAgain = true;
@@ -216,20 +236,9 @@
return NO_INIT;
}
- const int maxBufferCount = mCore->getMaxBufferCountLocked();
-
- // Free up any buffers that are in slots beyond the max buffer count
- for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- assert(mSlots[s].mBufferState.isFree());
- if (mSlots[s].mGraphicBuffer != NULL) {
- mCore->freeBufferLocked(s);
- *returnFlags |= RELEASE_ALL_BUFFERS;
- }
- }
-
int dequeuedCount = 0;
int acquiredCount = 0;
- for (int s = 0; s < maxBufferCount; ++s) {
+ for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isDequeued()) {
++dequeuedCount;
}
@@ -254,6 +263,7 @@
// our slots are empty but we have many buffers in the queue. This can
// cause us to run out of memory if we outrun the consumer. Wait here if
// it looks like we have too many buffers queued up.
+ const int maxBufferCount = mCore->getMaxBufferCountLocked();
bool tooManyBuffers = mCore->mQueue.size()
> static_cast<size_t>(maxBufferCount);
if (tooManyBuffers) {
@@ -268,15 +278,15 @@
} else {
if (caller == FreeSlotCaller::Dequeue) {
// If we're calling this from dequeue, prefer free buffers
- auto slot = getFreeBufferLocked();
+ int slot = getFreeBufferLocked();
if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = slot;
} else if (mCore->mAllowAllocation) {
- *found = getFreeSlotLocked(maxBufferCount);
+ *found = getFreeSlotLocked();
}
} else {
// If we're calling this from attach, prefer free slots
- auto slot = getFreeSlotLocked(maxBufferCount);
+ int slot = getFreeSlotLocked();
if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = slot;
} else {
@@ -369,7 +379,7 @@
int found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
- &found, &returnFlags);
+ &found);
if (status != NO_ERROR) {
return status;
}
@@ -388,24 +398,36 @@
// requested attributes, we free it and attempt to get another one.
if (!mCore->mAllowAllocation) {
if (buffer->needsReallocation(width, height, format, usage)) {
- if (mCore->mSingleBufferMode &&
- mCore->mSingleBufferSlot == found) {
+ if (mCore->mSingleBufferSlot == found) {
BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
"buffer");
return BAD_VALUE;
}
-
- mCore->freeBufferLocked(found);
+ mCore->mFreeSlots.insert(found);
+ mCore->clearBufferSlotLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT;
continue;
}
}
}
+ const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
+ if (mCore->mSingleBufferSlot == found &&
+ buffer->needsReallocation(width, height, format, usage)) {
+ BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
+ "buffer");
+
+ return BAD_VALUE;
+ }
+
+ if (mCore->mSingleBufferSlot != found) {
+ mCore->mActiveBuffers.insert(found);
+ }
*outSlot = found;
ATRACE_BUFFER_INDEX(found);
- attachedByConsumer = mSlots[found].mAttachedByConsumer;
+ attachedByConsumer = mSlots[found].mNeedsReallocation;
+ mSlots[found].mNeedsReallocation = false;
mSlots[found].mBufferState.dequeue();
@@ -417,7 +439,6 @@
mSlots[found].mBufferState.mShared = true;
}
- const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if ((buffer == NULL) ||
buffer->needsReallocation(width, height, format, usage))
{
@@ -452,8 +473,6 @@
*outFence = mSlots[found].mFence;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
-
- mCore->validateConsistencyLocked();
} // Autolock scope
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -481,6 +500,8 @@
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
+
+ VALIDATE_CONSISTENCY();
} // Autolock scope
}
@@ -527,9 +548,8 @@
return NO_INIT;
}
- if (mCore->mSingleBufferMode) {
- BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer"
- "mode");
+ if (mCore->mSingleBufferMode || mCore->mSingleBufferSlot == slot) {
+ BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer mode");
return BAD_VALUE;
}
@@ -548,9 +568,11 @@
}
mSlots[slot].mBufferState.detachProducer();
- mCore->freeBufferLocked(slot);
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeSlots.insert(slot);
+ mCore->clearBufferSlotLocked(slot);
mCore->mDequeueCondition.broadcast();
- mCore->validateConsistencyLocked();
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
@@ -593,13 +615,14 @@
int found = mCore->mFreeBuffers.front();
mCore->mFreeBuffers.remove(found);
+ mCore->mFreeSlots.insert(found);
BQ_LOGV("detachNextBuffer detached slot %d", found);
*outBuffer = mSlots[found].mGraphicBuffer;
*outFence = mSlots[found].mFence;
- mCore->freeBufferLocked(found);
- mCore->validateConsistencyLocked();
+ mCore->clearBufferSlotLocked(found);
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
@@ -644,8 +667,7 @@
status_t returnFlags = NO_ERROR;
int found;
- status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found,
- &returnFlags);
+ status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found);
if (status != NO_ERROR) {
return status;
}
@@ -666,8 +688,9 @@
mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
mSlots[*outSlot].mFence = Fence::NO_FENCE;
mSlots[*outSlot].mRequestBufferCalled = true;
-
- mCore->validateConsistencyLocked();
+ mSlots[*outSlot].mAcquireCalled = false;
+ mCore->mActiveBuffers.insert(found);
+ VALIDATE_CONSISTENCY();
return returnFlags;
}
@@ -722,11 +745,9 @@
return NO_INIT;
}
- const int maxBufferCount = mCore->getMaxBufferCountLocked();
-
- if (slot < 0 || slot >= maxBufferCount) {
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
- slot, maxBufferCount);
+ slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
} else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
@@ -807,9 +828,8 @@
// state to see if we need to replace it
BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
if (front->mIsDroppable) {
- // If the front queued buffer is still being tracked, we first
- // mark it as freed
- if (mCore->stillTracking(front)) {
+
+ if (!front->mIsStale) {
mSlots[front->mSlot].mBufferState.freeQueued();
// After leaving single buffer mode, the shared buffer will
@@ -821,9 +841,11 @@
}
// Don't put the shared buffer on the free list.
if (!mSlots[front->mSlot].mBufferState.isShared()) {
- mCore->mFreeBuffers.push_front(front->mSlot);
+ mCore->mActiveBuffers.erase(front->mSlot);
+ mCore->mFreeBuffers.push_back(front->mSlot);
}
}
+
// Overwrite the droppable buffer with the incoming one
*front = item;
frameReplacedListener = mCore->mConsumerListener;
@@ -845,7 +867,7 @@
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
- mCore->validateConsistencyLocked();
+ VALIDATE_CONSISTENCY();
} // Autolock scope
// Don't send the GraphicBuffer through the callback, and don't send
@@ -926,11 +948,13 @@
// Don't put the shared buffer on the free list.
if (!mSlots[slot].mBufferState.isShared()) {
- mCore->mFreeBuffers.push_front(slot);
+ mCore->mActiveBuffers.erase(slot);
+ mCore->mFreeBuffers.push_back(slot);
}
+
mSlots[slot].mFence = fence;
mCore->mDequeueCondition.broadcast();
- mCore->validateConsistencyLocked();
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
@@ -1020,6 +1044,17 @@
return BAD_VALUE;
}
+ int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode,
+ mDequeueTimeout < 0 ?
+ mCore->mConsumerControlledByApp && producerControlledByApp : false,
+ mCore->mMaxBufferCount) -
+ mCore->getMaxBufferCountLocked();
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
+ BQ_LOGE("connect: BufferQueue failed to adjust the number of available "
+ "slots. Delta = %d", delta);
+ return BAD_VALUE;
+ }
+
int status = NO_ERROR;
switch (api) {
case NATIVE_WINDOW_API_EGL:
@@ -1056,8 +1091,9 @@
mCore->mDequeueBufferCannotBlock =
mCore->mConsumerControlledByApp && producerControlledByApp;
}
- mCore->mAllowAllocation = true;
+ mCore->mAllowAllocation = true;
+ VALIDATE_CONSISTENCY();
return status;
}
@@ -1094,6 +1130,8 @@
token->unlinkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
}
+ mCore->mSingleBufferSlot =
+ BufferQueueCore::INVALID_BUFFER_SLOT;
mCore->mConnectedProducerListener = NULL;
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
mCore->mSidebandStream.clear();
@@ -1138,7 +1176,6 @@
PixelFormat format, uint32_t usage) {
ATRACE_CALL();
while (true) {
- Vector<int> freeSlots;
size_t newBufferCount = 0;
uint32_t allocWidth = 0;
uint32_t allocHeight = 0;
@@ -1154,32 +1191,11 @@
return;
}
- int currentBufferCount = 0;
- for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
- if (mSlots[slot].mGraphicBuffer != NULL) {
- ++currentBufferCount;
- } else {
- if (!mSlots[slot].mBufferState.isFree()) {
- BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
- slot);
- continue;
- }
-
- freeSlots.push_back(slot);
- }
- }
-
- int maxBufferCount = mCore->getMaxBufferCountLocked();
- BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
- currentBufferCount, maxBufferCount);
- if (maxBufferCount <= currentBufferCount)
- return;
- newBufferCount =
- static_cast<size_t>(maxBufferCount - currentBufferCount);
- if (freeSlots.size() < newBufferCount) {
- BQ_LOGE("allocateBuffers: ran out of free slots");
+ newBufferCount = mCore->mFreeSlots.size();
+ if (newBufferCount == 0) {
return;
}
+
allocWidth = width > 0 ? width : mCore->mDefaultWidth;
allocHeight = height > 0 ? height : mCore->mDefaultHeight;
allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
@@ -1221,29 +1237,28 @@
}
for (size_t i = 0; i < newBufferCount; ++i) {
- int slot = freeSlots[i];
- if (!mSlots[slot].mBufferState.isFree()) {
- // A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
- // allocated.
- BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
- "Dropping allocated buffer.", slot);
+ if (mCore->mFreeSlots.empty()) {
+ BQ_LOGV("allocateBuffers: a slot was occupied while "
+ "allocating. Dropping allocated buffer.");
continue;
}
- mCore->freeBufferLocked(slot); // Clean up the slot first
- mSlots[slot].mGraphicBuffer = buffers[i];
- mSlots[slot].mFence = Fence::NO_FENCE;
+ auto slot = mCore->mFreeSlots.begin();
+ mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
+ mSlots[*slot].mGraphicBuffer = buffers[i];
+ mSlots[*slot].mFence = Fence::NO_FENCE;
// freeBufferLocked puts this slot on the free slots list. Since
// we then attached a buffer, move the slot to free buffer list.
mCore->mFreeSlots.erase(slot);
- mCore->mFreeBuffers.push_front(slot);
+ mCore->mFreeBuffers.push_front(*slot);
- BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
+ BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d",
+ *slot);
}
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.broadcast();
- mCore->validateConsistencyLocked();
+ VALIDATE_CONSISTENCY();
} // Autolock scope
}
}
@@ -1297,8 +1312,18 @@
BQ_LOGV("setDequeueTimeout: %" PRId64, timeout);
Mutex::Autolock lock(mCore->mMutex);
+ int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false,
+ mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked();
+ if (!mCore->adjustAvailableSlotsLocked(delta)) {
+ BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of "
+ "available slots. Delta = %d", delta);
+ return BAD_VALUE;
+ }
+
mDequeueTimeout = timeout;
mCore->mDequeueBufferCannotBlock = false;
+
+ VALIDATE_CONSISTENCY();
return NO_ERROR;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 9e90ad0..6fc55c3 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -759,6 +759,13 @@
*outFence = Fence::NO_FENCE;
}
+ for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
+ if (mSlots[i].buffer != NULL &&
+ mSlots[i].buffer->handle == buffer->handle) {
+ mSlots[i].buffer = NULL;
+ }
+ }
+
return NO_ERROR;
}
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index b0798a1..f368d75 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -21,6 +21,7 @@
#include "egldefs.h"
#include <fcntl.h>
+#include <inttypes.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -306,7 +307,8 @@
// Sanity check the size before trying to mmap it.
size_t fileSize = statBuf.st_size;
if (fileSize > maxTotalSize * 2) {
- ALOGE("cache file is too large: %#llx", statBuf.st_size);
+ ALOGE("cache file is too large: %#" PRIx64,
+ static_cast<off64_t>(statBuf.st_size));
close(fd);
return;
}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index ab21c8f..6a9d7b6 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -271,7 +271,7 @@
// there are no reference to them, it which case, we're free to
// delete them.
size_t count = objects.size();
- ALOGW_IF(count, "eglTerminate() called w/ %d objects remaining", count);
+ ALOGW_IF(count, "eglTerminate() called w/ %zu objects remaining", count);
for (size_t i=0 ; i<count ; i++) {
egl_object_t* o = objects.itemAt(i);
o->destroy();
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index f5a9f58..17a8304 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -37,7 +37,7 @@
namespace android {
// ----------------------------------------------------------------------------
-struct egl_display_t;
+class egl_display_t;
class egl_object_t {
egl_display_t *display;
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 04919f7..eed14ab 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -134,7 +134,7 @@
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP: {
int32_t index = getMotionEventActionPointerIndex(action);
- return index >= 0 && size_t(index) < pointerCount;
+ return index >= 0 && index < pointerCount;
}
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 2d8eaef..7ae36d8 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -170,7 +170,7 @@
<< "Should reject motion events with pointer down index too large.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_POINTER_DOWN | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
@@ -191,7 +191,7 @@
<< "Should reject motion events with pointer up index too large.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- AMOTION_EVENT_ACTION_POINTER_UP | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 42bc865..a7fe69c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1528,8 +1528,8 @@
NotifySwitchArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ((1 << SW_LID) | (1 << SW_JACK_PHYSICAL_INSERT), args.switchValues);
- ASSERT_EQ((1 << SW_LID) | (1 << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
+ ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT), args.switchValues);
+ ASSERT_EQ((1U << SW_LID) | (1U << SW_JACK_PHYSICAL_INSERT) | (1 << SW_HEADPHONE_INSERT),
args.switchMask);
ASSERT_EQ(uint32_t(0), args.policyFlags);
}