--- /dev/null
+# Copyright 2012 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= atrace.cpp
+
+LOCAL_C_INCLUDES += external/zlib
+
+LOCAL_MODULE:= atrace
+
+LOCAL_MODULE_TAGS:= optional
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libcutils \
+ libutils \
+ libz \
+
+include $(BUILD_EXECUTABLE)
--- /dev/null
+
+ Copyright (c) 2012, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
--- /dev/null
+/*
+ * Copyright (C) 2012 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 <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sendfile.h>
+#include <time.h>
+#include <zlib.h>
+
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+
+#include <cutils/properties.h>
+
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+using namespace android;
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+enum { MAX_SYS_FILES = 8 };
+
+const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
+
+typedef enum { OPT, REQ } requiredness ;
+
+struct TracingCategory {
+ // The name identifying the category.
+ const char* name;
+
+ // A longer description of the category.
+ const char* longname;
+
+ // The userland tracing tags that the category enables.
+ uint64_t tags;
+
+ // The fname==NULL terminated list of /sys/ files that the category
+ // enables.
+ struct {
+ // Whether the file must be writable in order to enable the tracing
+ // category.
+ requiredness required;
+
+ // The path to the enable file.
+ const char* path;
+ } sysfiles[MAX_SYS_FILES];
+};
+
+/* Tracing categories */
+static const TracingCategory k_categories[] = {
+ { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } },
+ { "input", "Input", ATRACE_TAG_INPUT, { } },
+ { "view", "View System", ATRACE_TAG_VIEW, { } },
+ { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } },
+ { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } },
+ { "audio", "Audio", ATRACE_TAG_AUDIO, { } },
+ { "video", "Video", ATRACE_TAG_VIDEO, { } },
+ { "camera", "Camera", ATRACE_TAG_CAMERA, { } },
+ { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } },
+ { "sched", "CPU Scheduling", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/sched/sched_switch/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/sched/sched_wakeup/enable" },
+ } },
+ { "freq", "CPU Frequency", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/power/cpu_frequency/enable" },
+ { OPT, "/sys/kernel/debug/tracing/events/power/clock_set_rate/enable" },
+ } },
+ { "membus", "Memory Bus Utilization", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/memory_bus/enable" },
+ } },
+ { "idle", "CPU Idle", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/power/cpu_idle/enable" },
+ } },
+ { "disk", "Disk I/O", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/ext4/ext4_sync_file_exit/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_issue/enable" },
+ { REQ, "/sys/kernel/debug/tracing/events/block/block_rq_complete/enable" },
+ } },
+ { "load", "CPU Load", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/cpufreq_interactive/enable" },
+ } },
+ { "sync", "Synchronization", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/sync/enable" },
+ } },
+ { "workq", "Kernel Workqueues", 0, {
+ { REQ, "/sys/kernel/debug/tracing/events/workqueue/enable" },
+ } },
+};
+
+/* Command line options */
+static int g_traceDurationSeconds = 5;
+static bool g_traceOverwrite = false;
+static int g_traceBufferSizeKB = 2048;
+static bool g_compress = false;
+static bool g_nohup = false;
+static int g_initialSleepSecs = 0;
+
+/* Global state */
+static bool g_traceAborted = false;
+static bool g_categoryEnables[NELEM(k_categories)] = {};
+
+/* Sys file paths */
+static const char* k_traceClockPath =
+ "/sys/kernel/debug/tracing/trace_clock";
+
+static const char* k_traceBufferSizePath =
+ "/sys/kernel/debug/tracing/buffer_size_kb";
+
+static const char* k_tracingOverwriteEnablePath =
+ "/sys/kernel/debug/tracing/options/overwrite";
+
+static const char* k_tracingOnPath =
+ "/sys/kernel/debug/tracing/tracing_on";
+
+static const char* k_tracePath =
+ "/sys/kernel/debug/tracing/trace";
+
+// Check whether a file exists.
+static bool fileExists(const char* filename) {
+ return access(filename, F_OK) != -1;
+}
+
+// Check whether a file is writable.
+static bool fileIsWritable(const char* filename) {
+ return access(filename, W_OK) != -1;
+}
+
+// Write a string to a file, returning true if the write was successful.
+static bool writeStr(const char* filename, const char* str)
+{
+ int fd = open(filename, O_WRONLY);
+ if (fd == -1) {
+ fprintf(stderr, "error opening %s: %s (%d)\n", filename,
+ strerror(errno), errno);
+ return false;
+ }
+
+ bool ok = true;
+ ssize_t len = strlen(str);
+ if (write(fd, str, len) != len) {
+ fprintf(stderr, "error writing to %s: %s (%d)\n", filename,
+ strerror(errno), errno);
+ ok = false;
+ }
+
+ close(fd);
+
+ return ok;
+}
+
+// Enable or disable a kernel option by writing a "1" or a "0" into a /sys
+// file.
+static bool setKernelOptionEnable(const char* filename, bool enable)
+{
+ return writeStr(filename, enable ? "1" : "0");
+}
+
+// Check whether the category is supported on the device with the current
+// rootness. A category is supported only if all its required /sys/ files are
+// writable and if enabling the category will enable one or more tracing tags
+// or /sys/ files.
+static bool isCategorySupported(const TracingCategory& category)
+{
+ bool ok = category.tags != 0;
+ for (int i = 0; i < MAX_SYS_FILES; i++) {
+ const char* path = category.sysfiles[i].path;
+ bool req = category.sysfiles[i].required == REQ;
+ if (path != NULL) {
+ if (req) {
+ if (!fileIsWritable(path)) {
+ return false;
+ } else {
+ ok = true;
+ }
+ } else {
+ ok |= fileIsWritable(path);
+ }
+ }
+ }
+ return ok;
+}
+
+// Check whether the category would be supported on the device if the user
+// were root. This function assumes that root is able to write to any file
+// that exists. It performs the same logic as isCategorySupported, but it
+// uses file existance rather than writability in the /sys/ file checks.
+static bool isCategorySupportedForRoot(const TracingCategory& category)
+{
+ bool ok = category.tags != 0;
+ for (int i = 0; i < MAX_SYS_FILES; i++) {
+ const char* path = category.sysfiles[i].path;
+ bool req = category.sysfiles[i].required == REQ;
+ if (path != NULL) {
+ if (req) {
+ if (!fileExists(path)) {
+ return false;
+ } else {
+ ok = true;
+ }
+ } else {
+ ok |= fileExists(path);
+ }
+ }
+ }
+ return ok;
+}
+
+// Enable or disable overwriting of the kernel trace buffers. Disabling this
+// will cause tracing to stop once the trace buffers have filled up.
+static bool setTraceOverwriteEnable(bool enable)
+{
+ return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable);
+}
+
+// Enable or disable kernel tracing.
+static bool setTracingEnabled(bool enable)
+{
+ return setKernelOptionEnable(k_tracingOnPath, enable);
+}
+
+// Clear the contents of the kernel trace.
+static bool clearTrace()
+{
+ int traceFD = creat(k_tracePath, 0);
+ if (traceFD == -1) {
+ fprintf(stderr, "error truncating %s: %s (%d)\n", k_tracePath,
+ strerror(errno), errno);
+ return false;
+ }
+
+ close(traceFD);
+
+ return true;
+}
+
+// Set the size of the kernel's trace buffer in kilobytes.
+static bool setTraceBufferSizeKB(int size)
+{
+ char str[32] = "1";
+ int len;
+ if (size < 1) {
+ size = 1;
+ }
+ snprintf(str, 32, "%d", size);
+ return writeStr(k_traceBufferSizePath, str);
+}
+
+// Enable or disable the kernel's use of the global clock. Disabling the global
+// clock will result in the kernel using a per-CPU local clock.
+static bool setGlobalClockEnable(bool enable)
+{
+ return writeStr(k_traceClockPath, enable ? "global" : "local");
+}
+
+// Poke all the binder-enabled processes in the system to get them to re-read
+// their system properties.
+static bool pokeBinderServices()
+{
+ sp<IServiceManager> sm = defaultServiceManager();
+ Vector<String16> services = sm->listServices();
+ for (size_t i = 0; i < services.size(); i++) {
+ sp<IBinder> obj = sm->checkService(services[i]);
+ if (obj != NULL) {
+ Parcel data;
+ if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data,
+ NULL, 0) != OK) {
+ if (false) {
+ // XXX: For some reason this fails on tablets trying to
+ // poke the "phone" service. It's not clear whether some
+ // are expected to fail.
+ String8 svc(services[i]);
+ fprintf(stderr, "error poking binder service %s\n",
+ svc.string());
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// Set the trace tags that userland tracing uses, and poke the running
+// processes to pick up the new value.
+static bool setTagsProperty(uint64_t tags)
+{
+ char buf[64];
+ snprintf(buf, 64, "%#llx", tags);
+ if (property_set(k_traceTagsProperty, buf) < 0) {
+ fprintf(stderr, "error setting trace tags system property\n");
+ return false;
+ }
+ return pokeBinderServices();
+}
+
+// Disable all /sys/ enable files.
+static bool disableKernelTraceEvents() {
+ bool ok = true;
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ const TracingCategory &c = k_categories[i];
+ for (int j = 0; j < MAX_SYS_FILES; j++) {
+ const char* path = c.sysfiles[j].path;
+ if (path != NULL && fileIsWritable(path)) {
+ ok &= setKernelOptionEnable(path, false);
+ }
+ }
+ }
+ return ok;
+}
+
+// Enable tracing in the kernel.
+static bool startTrace()
+{
+ bool ok = true;
+
+ // Set up the tracing options.
+ ok &= setTraceOverwriteEnable(g_traceOverwrite);
+ ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
+ ok &= setGlobalClockEnable(true);
+
+ // Set up the tags property.
+ uint64_t tags = 0;
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ if (g_categoryEnables[i]) {
+ const TracingCategory &c = k_categories[i];
+ tags |= c.tags;
+ }
+ }
+ ok &= setTagsProperty(tags);
+
+ // Disable all the sysfs enables. This is done as a separate loop from
+ // the enables to allow the same enable to exist in multiple categories.
+ ok &= disableKernelTraceEvents();
+
+ // Enable all the sysfs enables that are in an enabled category.
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ if (g_categoryEnables[i]) {
+ const TracingCategory &c = k_categories[i];
+ for (int j = 0; j < MAX_SYS_FILES; j++) {
+ const char* path = c.sysfiles[j].path;
+ bool required = c.sysfiles[j].required == REQ;
+ if (path != NULL) {
+ if (fileIsWritable(path)) {
+ ok &= setKernelOptionEnable(path, true);
+ } else if (required) {
+ fprintf(stderr, "error writing file %s\n", path);
+ ok = false;
+ }
+ }
+ }
+ }
+ }
+
+ // Enable tracing.
+ ok &= setTracingEnabled(true);
+
+ return ok;
+}
+
+// Disable tracing in the kernel.
+static void stopTrace()
+{
+ // Disable tracing.
+ setTracingEnabled(false);
+
+ // Disable all tracing that we're able to.
+ disableKernelTraceEvents();
+
+ // Disable all the trace tags.
+ setTagsProperty(0);
+
+ // Set the options back to their defaults.
+ setTraceOverwriteEnable(true);
+ setGlobalClockEnable(false);
+
+ // Note that we can't reset the trace buffer size here because that would
+ // clear the trace before we've read it.
+}
+
+// Read the current kernel trace and write it to stdout.
+static void dumpTrace()
+{
+ int traceFD = open(k_tracePath, O_RDWR);
+ if (traceFD == -1) {
+ fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath,
+ strerror(errno), errno);
+ return;
+ }
+
+ if (g_compress) {
+ z_stream zs;
+ uint8_t *in, *out;
+ int result, flush;
+
+ bzero(&zs, sizeof(zs));
+ result = deflateInit(&zs, Z_DEFAULT_COMPRESSION);
+ if (result != Z_OK) {
+ fprintf(stderr, "error initializing zlib: %d\n", result);
+ close(traceFD);
+ return;
+ }
+
+ const size_t bufSize = 64*1024;
+ in = (uint8_t*)malloc(bufSize);
+ out = (uint8_t*)malloc(bufSize);
+ flush = Z_NO_FLUSH;
+
+ zs.next_out = out;
+ zs.avail_out = bufSize;
+
+ do {
+
+ if (zs.avail_in == 0) {
+ // More input is needed.
+ result = read(traceFD, in, bufSize);
+ if (result < 0) {
+ fprintf(stderr, "error reading trace: %s (%d)\n",
+ strerror(errno), errno);
+ result = Z_STREAM_END;
+ break;
+ } else if (result == 0) {
+ flush = Z_FINISH;
+ } else {
+ zs.next_in = in;
+ zs.avail_in = result;
+ }
+ }
+
+ if (zs.avail_out == 0) {
+ // Need to write the output.
+ result = write(STDOUT_FILENO, out, bufSize);
+ if ((size_t)result < bufSize) {
+ fprintf(stderr, "error writing deflated trace: %s (%d)\n",
+ strerror(errno), errno);
+ result = Z_STREAM_END; // skip deflate error message
+ zs.avail_out = bufSize; // skip the final write
+ break;
+ }
+ zs.next_out = out;
+ zs.avail_out = bufSize;
+ }
+
+ } while ((result = deflate(&zs, flush)) == Z_OK);
+
+ if (result != Z_STREAM_END) {
+ fprintf(stderr, "error deflating trace: %s\n", zs.msg);
+ }
+
+ if (zs.avail_out < bufSize) {
+ size_t bytes = bufSize - zs.avail_out;
+ result = write(STDOUT_FILENO, out, bytes);
+ if ((size_t)result < bytes) {
+ fprintf(stderr, "error writing deflated trace: %s (%d)\n",
+ strerror(errno), errno);
+ }
+ }
+
+ result = deflateEnd(&zs);
+ if (result != Z_OK) {
+ fprintf(stderr, "error cleaning up zlib: %d\n", result);
+ }
+
+ free(in);
+ free(out);
+ } else {
+ ssize_t sent = 0;
+ while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0);
+ if (sent == -1) {
+ fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno),
+ errno);
+ }
+ }
+
+ close(traceFD);
+}
+
+static void handleSignal(int signo)
+{
+ if (!g_nohup) {
+ g_traceAborted = true;
+ }
+}
+
+static void registerSigHandler()
+{
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = handleSignal;
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+}
+
+static bool setCategoryEnable(const char* name, bool enable)
+{
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ const TracingCategory& c = k_categories[i];
+ if (strcmp(name, c.name) == 0) {
+ if (isCategorySupported(c)) {
+ g_categoryEnables[i] = enable;
+ return true;
+ } else {
+ if (isCategorySupportedForRoot(c)) {
+ fprintf(stderr, "error: category \"%s\" requires root "
+ "privileges.\n", name);
+ } else {
+ fprintf(stderr, "error: category \"%s\" is not supported "
+ "on this device.\n", name);
+ }
+ return false;
+ }
+ }
+ }
+ fprintf(stderr, "error: unknown tracing category \"%s\"\n", name);
+ return false;
+}
+
+static void listSupportedCategories()
+{
+ for (int i = 0; i < NELEM(k_categories); i++) {
+ const TracingCategory& c = k_categories[i];
+ if (isCategorySupported(c)) {
+ printf(" %10s - %s\n", c.name, c.longname);
+ }
+ }
+}
+
+// Print the command usage help to stderr.
+static void showHelp(const char *cmd)
+{
+ fprintf(stderr, "usage: %s [options] [categories...]\n", cmd);
+ fprintf(stderr, "options include:\n"
+ " -b N use a trace buffer size of N KB\n"
+ " -c trace into a circular buffer\n"
+ " -n ignore signals\n"
+ " -s N sleep for N seconds before tracing [default 0]\n"
+ " -t N trace for N seconds [defualt 5]\n"
+ " -z compress the trace dump\n"
+ " --async_start start circular trace and return immediatly\n"
+ " --async_dump dump the current contents of circular trace buffer\n"
+ " --async_stop stop tracing and dump the current contents of circular\n"
+ " trace buffer\n"
+ " --list_categories\n"
+ " list the available tracing categories\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+ bool async = false;
+ bool traceStart = true;
+ bool traceStop = true;
+ bool traceDump = true;
+
+ if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+ showHelp(argv[0]);
+ exit(0);
+ }
+
+ for (;;) {
+ int ret;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"async_start", no_argument, 0, 0 },
+ {"async_stop", no_argument, 0, 0 },
+ {"async_dump", no_argument, 0, 0 },
+ {"list_categories", no_argument, 0, 0 },
+ { 0, 0, 0, 0 }
+ };
+
+ ret = getopt_long(argc, argv, "b:cns:t:z",
+ long_options, &option_index);
+
+ if (ret < 0) {
+ for (int i = optind; i < argc; i++) {
+ if (!setCategoryEnable(argv[i], true)) {
+ fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]);
+ exit(1);
+ }
+ }
+ break;
+ }
+
+ switch(ret) {
+ case 'b':
+ g_traceBufferSizeKB = atoi(optarg);
+ break;
+
+ case 'c':
+ g_traceOverwrite = true;
+ break;
+
+ case 'n':
+ g_nohup = true;
+ break;
+
+ case 's':
+ g_initialSleepSecs = atoi(optarg);
+ break;
+
+ case 't':
+ g_traceDurationSeconds = atoi(optarg);
+ break;
+
+ case 'z':
+ g_compress = true;
+ break;
+
+ case 0:
+ if (!strcmp(long_options[option_index].name, "async_start")) {
+ async = true;
+ traceStop = false;
+ traceDump = false;
+ g_traceOverwrite = true;
+ } else if (!strcmp(long_options[option_index].name, "async_stop")) {
+ async = true;
+ traceStop = false;
+ } else if (!strcmp(long_options[option_index].name, "async_dump")) {
+ async = true;
+ traceStart = false;
+ traceStop = false;
+ } else if (!strcmp(long_options[option_index].name, "list_categories")) {
+ listSupportedCategories();
+ exit(0);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "\n");
+ showHelp(argv[0]);
+ exit(-1);
+ break;
+ }
+ }
+
+ registerSigHandler();
+
+ if (g_initialSleepSecs > 0) {
+ sleep(g_initialSleepSecs);
+ }
+
+ bool ok = startTrace();
+
+ if (ok && traceStart) {
+ printf("capturing trace...");
+ fflush(stdout);
+
+ // We clear the trace after starting it because tracing gets enabled for
+ // each CPU individually in the kernel. Having the beginning of the trace
+ // contain entries from only one CPU can cause "begin" entries without a
+ // matching "end" entry to show up if a task gets migrated from one CPU to
+ // another.
+ ok = clearTrace();
+
+ if (ok && !async) {
+ // Sleep to allow the trace to be captured.
+ struct timespec timeLeft;
+ timeLeft.tv_sec = g_traceDurationSeconds;
+ timeLeft.tv_nsec = 0;
+ do {
+ if (g_traceAborted) {
+ break;
+ }
+ } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR);
+ }
+ }
+
+ // Stop the trace and restore the default settings.
+ if (traceStop)
+ stopTrace();
+
+ if (ok && traceDump) {
+ if (!g_traceAborted) {
+ printf(" done\nTRACE:\n");
+ fflush(stdout);
+ dumpTrace();
+ } else {
+ printf("\ntrace aborted.\n");
+ fflush(stdout);
+ }
+ clearTrace();
+ } else if (!ok) {
+ fprintf(stderr, "unable to start tracing\n");
+ }
+
+ // Reset the trace buffer size to 1.
+ if (traceStop)
+ setTraceBufferSizeKB(1);
+
+ return g_traceAborted ? 1 : 0;
+}
--- /dev/null
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= bugreport.c
+
+LOCAL_MODULE:= bugreport
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
--- /dev/null
+/*
+ * Copyright (C) 2009 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+int main(int argc, char *argv[]) {
+ char buffer[65536];
+ int i, s;
+
+ /* start the dumpstate service */
+ property_set("ctl.start", "dumpstate");
+
+ /* socket will not be available until service starts */
+ for (i = 0; i < 10; i++) {
+ s = socket_local_client("dumpstate",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (s >= 0)
+ break;
+ /* try again in 1 second */
+ sleep(1);
+ }
+
+ if (s < 0) {
+ fprintf(stderr, "Failed to connect to dumpstate service\n");
+ exit(1);
+ }
+
+ while (1) {
+ int length = read(s, buffer, sizeof(buffer));
+ if (length <= 0)
+ break;
+ fwrite(buffer, 1, length, stdout);
+ }
+
+ close(s);
+ return 0;
+}
run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
- for_each_pid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
+ for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
// dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
- run_command("FILESYSTEMS & FREE SPACE", 10, SU_PATH, "root", "df", NULL);
+ run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
run_command("PACKAGE SETTINGS", 20, SU_PATH, "root", "cat", "/data/system/packages.xml", NULL);
dump_file("PACKAGE UID ERRORS", "/data/system/uiderrors.txt");
#include <time.h>
#include <unistd.h>
+#include <stdbool.h>
#include <stdio.h>
#define SU_PATH "/system/xbin/su"
+typedef void (for_each_pid_func)(int, const char *);
+typedef void (for_each_tid_func)(int, int, const char *);
+
/* prints the contents of a file */
int dump_file(const char *title, const char* path);
const char *dump_traces();
/* for each process in the system, run the specified function */
-void for_each_pid(void (*func)(int, const char *), const char *header);
+void for_each_pid(for_each_pid_func func, const char *header);
+
+/* for each thread in the system, run the specified function */
+void for_each_tid(for_each_tid_func func, const char *header);
/* Displays a blocked processes in-kernel wait channel */
-void show_wchan(int pid, const char *name);
+void show_wchan(int pid, int tid, const char *name);
/* Runs "showmap" for a process */
void do_showmap(int pid, const char *name);
NULL,
};
-void for_each_pid(void (*func)(int, const char *), const char *header) {
+static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) {
DIR *d;
struct dirent *de;
if ((fd = open(cmdpath, O_RDONLY)) < 0) {
strcpy(cmdline, "N/A");
} else {
- read(fd, cmdline, sizeof(cmdline));
+ read(fd, cmdline, sizeof(cmdline) - 1);
close(fd);
}
- func(pid, cmdline);
+ helper(pid, cmdline, arg);
}
closedir(d);
}
-void show_wchan(int pid, const char *name) {
+static void for_each_pid_helper(int pid, const char *cmdline, void *arg) {
+ for_each_pid_func *func = arg;
+ func(pid, cmdline);
+}
+
+void for_each_pid(for_each_pid_func func, const char *header) {
+ __for_each_pid(for_each_pid_helper, header, func);
+}
+
+static void for_each_tid_helper(int pid, const char *cmdline, void *arg) {
+ DIR *d;
+ struct dirent *de;
+ char taskpath[255];
+ for_each_tid_func *func = arg;
+
+ sprintf(taskpath, "/proc/%d/task", pid);
+
+ if (!(d = opendir(taskpath))) {
+ printf("Failed to open %s (%s)\n", taskpath, strerror(errno));
+ return;
+ }
+
+ func(pid, pid, cmdline);
+
+ while ((de = readdir(d))) {
+ int tid;
+ int fd;
+ char commpath[255];
+ char comm[255];
+
+ if (!(tid = atoi(de->d_name))) {
+ continue;
+ }
+
+ if (tid == pid)
+ continue;
+
+ sprintf(commpath,"/proc/%d/comm", tid);
+ memset(comm, 0, sizeof(comm));
+ if ((fd = open(commpath, O_RDONLY)) < 0) {
+ strcpy(comm, "N/A");
+ } else {
+ char *c;
+ read(fd, comm, sizeof(comm) - 1);
+ close(fd);
+
+ c = strrchr(comm, '\n');
+ if (c) {
+ *c = '\0';
+ }
+ }
+ func(pid, tid, comm);
+ }
+
+ closedir(d);
+}
+
+void for_each_tid(for_each_tid_func func, const char *header) {
+ __for_each_pid(for_each_tid_helper, header, func);
+}
+
+void show_wchan(int pid, int tid, const char *name) {
char path[255];
char buffer[255];
int fd;
+ char name_buffer[255];
memset(buffer, 0, sizeof(buffer));
- sprintf(path, "/proc/%d/wchan", pid);
+ sprintf(path, "/proc/%d/wchan", tid);
if ((fd = open(path, O_RDONLY)) < 0) {
printf("Failed to open '%s' (%s)\n", path, strerror(errno));
return;
goto out_close;
}
- printf("%-7d %-32s %s\n", pid, name, buffer);
+ snprintf(name_buffer, sizeof(name_buffer), "%*s%s",
+ pid == tid ? 0 : 3, "", name);
+
+ printf("%-7d %-32s %s\n", tid, name_buffer, buffer);
out_close:
close(fd);
--- /dev/null
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ Composers.cpp \
+ GLHelper.cpp \
+ Renderers.cpp \
+ Main.cpp \
+
+LOCAL_MODULE:= flatland
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SHARED_LIBRARIES := \
+ libEGL \
+ libGLESv2 \
+ libcutils \
+ libgui \
+ libui \
+ libutils \
+
+include $(BUILD_EXECUTABLE)
--- /dev/null
+/*
+ * Copyright (C) 2012 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 "Flatland.h"
+#include "GLHelper.h"
+
+namespace android {
+
+class Blitter {
+public:
+
+ bool setUp(GLHelper* helper) {
+ bool result;
+
+ result = helper->getShaderProgram("Blit", &mBlitPgm);
+ if (!result) {
+ return false;
+ }
+
+ mPosAttribLoc = glGetAttribLocation(mBlitPgm, "position");
+ mUVAttribLoc = glGetAttribLocation(mBlitPgm, "uv");
+ mUVToTexUniformLoc = glGetUniformLocation(mBlitPgm, "uvToTex");
+ mObjToNdcUniformLoc = glGetUniformLocation(mBlitPgm, "objToNdc");
+ mBlitSrcSamplerLoc = glGetUniformLocation(mBlitPgm, "blitSrc");
+ mModColorUniformLoc = glGetUniformLocation(mBlitPgm, "modColor");
+
+ return true;
+ }
+
+ bool blit(GLuint texName, const float* texMatrix,
+ int32_t x, int32_t y, uint32_t w, uint32_t h) {
+ float modColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ return modBlit(texName, texMatrix, modColor, x, y, w, h);
+ }
+
+ bool modBlit(GLuint texName, const float* texMatrix, float* modColor,
+ int32_t x, int32_t y, uint32_t w, uint32_t h) {
+ glUseProgram(mBlitPgm);
+
+ GLint vp[4];
+ glGetIntegerv(GL_VIEWPORT, vp);
+ float screenToNdc[16] = {
+ 2.0f/float(vp[2]), 0.0f, 0.0f, 0.0f,
+ 0.0f, -2.0f/float(vp[3]), 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ -1.0f, 1.0f, 0.0f, 1.0f,
+ };
+ const float pos[] = {
+ float(x), float(y),
+ float(x+w), float(y),
+ float(x), float(y+h),
+ float(x+w), float(y+h),
+ };
+ const float uv[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ };
+
+ glVertexAttribPointer(mPosAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, pos);
+ glVertexAttribPointer(mUVAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, uv);
+ glEnableVertexAttribArray(mPosAttribLoc);
+ glEnableVertexAttribArray(mUVAttribLoc);
+
+ glUniformMatrix4fv(mObjToNdcUniformLoc, 1, GL_FALSE, screenToNdc);
+ glUniformMatrix4fv(mUVToTexUniformLoc, 1, GL_FALSE, texMatrix);
+ glUniform4fv(mModColorUniformLoc, 1, modColor);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, texName);
+ glUniform1i(mBlitSrcSamplerLoc, 0);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glDisableVertexAttribArray(mPosAttribLoc);
+ glDisableVertexAttribArray(mUVAttribLoc);
+
+ if (glGetError() != GL_NO_ERROR) {
+ fprintf(stderr, "GL error!\n");
+ }
+
+ return true;
+ }
+
+private:
+ GLuint mBlitPgm;
+ GLint mPosAttribLoc;
+ GLint mUVAttribLoc;
+ GLint mUVToTexUniformLoc;
+ GLint mObjToNdcUniformLoc;
+ GLint mBlitSrcSamplerLoc;
+ GLint mModColorUniformLoc;
+};
+
+class ComposerBase : public Composer {
+public:
+ virtual ~ComposerBase() {}
+
+ virtual bool setUp(const LayerDesc& desc,
+ GLHelper* helper) {
+ mLayerDesc = desc;
+ return setUp(helper);
+ }
+
+ virtual void tearDown() {
+ }
+
+ virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
+ return true;
+ }
+
+protected:
+ virtual bool setUp(GLHelper* helper) {
+ return true;
+ }
+
+ LayerDesc mLayerDesc;
+};
+
+Composer* nocomp() {
+ class NoComp : public ComposerBase {
+ };
+ return new NoComp();
+}
+
+Composer* opaque() {
+ class OpaqueComp : public ComposerBase {
+ virtual bool setUp(GLHelper* helper) {
+ return mBlitter.setUp(helper);
+ }
+
+ virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
+ float texMatrix[16];
+ glc->getTransformMatrix(texMatrix);
+
+ int32_t x = mLayerDesc.x;
+ int32_t y = mLayerDesc.y;
+ int32_t w = mLayerDesc.width;
+ int32_t h = mLayerDesc.height;
+
+ return mBlitter.blit(texName, texMatrix, x, y, w, h);
+ }
+
+ Blitter mBlitter;
+ };
+ return new OpaqueComp();
+}
+
+Composer* opaqueShrink() {
+ class OpaqueComp : public ComposerBase {
+ virtual bool setUp(GLHelper* helper) {
+ mParity = false;
+ return mBlitter.setUp(helper);
+ }
+
+ virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
+ float texMatrix[16];
+ glc->getTransformMatrix(texMatrix);
+
+ int32_t x = mLayerDesc.x;
+ int32_t y = mLayerDesc.y;
+ int32_t w = mLayerDesc.width;
+ int32_t h = mLayerDesc.height;
+
+ mParity = !mParity;
+ if (mParity) {
+ x += w / 128;
+ y += h / 128;
+ w -= w / 64;
+ h -= h / 64;
+ }
+
+ return mBlitter.blit(texName, texMatrix, x, y, w, h);
+ }
+
+ Blitter mBlitter;
+ bool mParity;
+ };
+ return new OpaqueComp();
+}
+
+Composer* blend() {
+ class BlendComp : public ComposerBase {
+ virtual bool setUp(GLHelper* helper) {
+ return mBlitter.setUp(helper);
+ }
+
+ virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
+ bool result;
+
+ float texMatrix[16];
+ glc->getTransformMatrix(texMatrix);
+
+ float modColor[4] = { .75f, .75f, .75f, .75f };
+
+ int32_t x = mLayerDesc.x;
+ int32_t y = mLayerDesc.y;
+ int32_t w = mLayerDesc.width;
+ int32_t h = mLayerDesc.height;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ result = mBlitter.modBlit(texName, texMatrix, modColor,
+ x, y, w, h);
+ if (!result) {
+ return false;
+ }
+
+ glDisable(GL_BLEND);
+
+ return true;
+ }
+
+ Blitter mBlitter;
+ };
+ return new BlendComp();
+}
+
+Composer* blendShrink() {
+ class BlendShrinkComp : public ComposerBase {
+ virtual bool setUp(GLHelper* helper) {
+ mParity = false;
+ return mBlitter.setUp(helper);
+ }
+
+ virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) {
+ bool result;
+
+ float texMatrix[16];
+ glc->getTransformMatrix(texMatrix);
+
+ float modColor[4] = { .75f, .75f, .75f, .75f };
+
+ int32_t x = mLayerDesc.x;
+ int32_t y = mLayerDesc.y;
+ int32_t w = mLayerDesc.width;
+ int32_t h = mLayerDesc.height;
+
+ mParity = !mParity;
+ if (mParity) {
+ x += w / 128;
+ y += h / 128;
+ w -= w / 64;
+ h -= h / 64;
+ }
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ result = mBlitter.modBlit(texName, texMatrix, modColor,
+ x, y, w, h);
+ if (!result) {
+ return false;
+ }
+
+ glDisable(GL_BLEND);
+
+ return true;
+ }
+
+ Blitter mBlitter;
+ bool mParity;
+ };
+ return new BlendShrinkComp();
+}
+
+} // namespace android
--- /dev/null
+/*
+ * Copyright (C) 2012 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 <stdint.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include <gui/GLConsumer.h>
+
+namespace android {
+
+#define NELEMS(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+enum { MAX_NUM_LAYERS = 16 };
+enum { MAX_TEST_RUNS = 16 };
+
+class Composer;
+class Renderer;
+class GLHelper;
+
+struct LayerDesc {
+ uint32_t flags;
+ Renderer* (*rendererFactory)();
+ Composer* (*composerFactory)();
+ int32_t x;
+ int32_t y;
+ uint32_t width;
+ uint32_t height;
+};
+
+void resetColorGenerator();
+
+class Composer {
+public:
+ virtual ~Composer() {}
+ virtual bool setUp(const LayerDesc& desc, GLHelper* helper) = 0;
+ virtual void tearDown() = 0;
+ virtual bool compose(GLuint texName, const sp<GLConsumer>& glc) = 0;
+};
+
+Composer* nocomp();
+Composer* opaque();
+Composer* opaqueShrink();
+Composer* blend();
+Composer* blendShrink();
+
+class Renderer {
+public:
+ virtual ~Renderer() {}
+ virtual bool setUp(GLHelper* helper) = 0;
+ virtual void tearDown() = 0;
+ virtual bool render(EGLSurface surface) = 0;
+};
+
+Renderer* staticGradient();
+
+} // namespace android
--- /dev/null
+/*
+ * Copyright (C) 2012 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 <ui/DisplayInfo.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include "GLHelper.h"
+
+ namespace android {
+
+GLHelper::GLHelper() :
+ mGraphicBufferAlloc(new GraphicBufferAlloc()),
+ mDisplay(EGL_NO_DISPLAY),
+ mContext(EGL_NO_CONTEXT),
+ mDummySurface(EGL_NO_SURFACE),
+ mConfig(0),
+ mShaderPrograms(NULL),
+ mDitherTexture(0) {
+}
+
+GLHelper::~GLHelper() {
+}
+
+bool GLHelper::setUp(const ShaderDesc* shaderDescs, size_t numShaders) {
+ bool result;
+
+ mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mDisplay == EGL_NO_DISPLAY) {
+ fprintf(stderr, "eglGetDisplay error: %#x\n", eglGetError());
+ return false;
+ }
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ result = eglInitialize(mDisplay, &majorVersion, &minorVersion);
+ if (result != EGL_TRUE) {
+ fprintf(stderr, "eglInitialize error: %#x\n", eglGetError());
+ return false;
+ }
+
+ EGLint numConfigs = 0;
+ EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_NONE
+ };
+ result = eglChooseConfig(mDisplay, configAttribs, &mConfig, 1,
+ &numConfigs);
+ if (result != EGL_TRUE) {
+ fprintf(stderr, "eglChooseConfig error: %#x\n", eglGetError());
+ return false;
+ }
+
+ EGLint contextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+ mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT,
+ contextAttribs);
+ if (mContext == EGL_NO_CONTEXT) {
+ fprintf(stderr, "eglCreateContext error: %#x\n", eglGetError());
+ return false;
+ }
+
+ bool resultb = createNamedSurfaceTexture(0, 1, 1, &mDummyGLConsumer,
+ &mDummySurface);
+ if (!resultb) {
+ return false;
+ }
+
+ resultb = makeCurrent(mDummySurface);
+ if (!resultb) {
+ return false;
+ }
+
+ resultb = setUpShaders(shaderDescs, numShaders);
+ if (!resultb) {
+ return false;
+ }
+
+ return true;
+}
+
+void GLHelper::tearDown() {
+ if (mShaderPrograms != NULL) {
+ delete[] mShaderPrograms;
+ mShaderPrograms = NULL;
+ }
+
+ if (mSurfaceComposerClient != NULL) {
+ mSurfaceComposerClient->dispose();
+ mSurfaceComposerClient.clear();
+ }
+
+ if (mDisplay != EGL_NO_DISPLAY) {
+ eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ }
+
+ if (mContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mDisplay, mContext);
+ }
+
+ if (mDummySurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mDisplay, mDummySurface);
+ }
+
+ mDisplay = EGL_NO_DISPLAY;
+ mContext = EGL_NO_CONTEXT;
+ mDummySurface = EGL_NO_SURFACE;
+ mDummyGLConsumer.clear();
+ mConfig = 0;
+}
+
+bool GLHelper::makeCurrent(EGLSurface surface) {
+ EGLint result;
+
+ result = eglMakeCurrent(mDisplay, surface, surface, mContext);
+ if (result != EGL_TRUE) {
+ fprintf(stderr, "eglMakeCurrent error: %#x\n", eglGetError());
+ return false;
+ }
+
+ EGLint w, h;
+ eglQuerySurface(mDisplay, surface, EGL_WIDTH, &w);
+ eglQuerySurface(mDisplay, surface, EGL_HEIGHT, &h);
+ glViewport(0, 0, w, h);
+
+ return true;
+}
+
+bool GLHelper::createSurfaceTexture(uint32_t w, uint32_t h,
+ sp<GLConsumer>* glConsumer, EGLSurface* surface,
+ GLuint* name) {
+ if (!makeCurrent(mDummySurface)) {
+ return false;
+ }
+
+ *name = 0;
+ glGenTextures(1, name);
+ if (*name == 0) {
+ fprintf(stderr, "glGenTextures error: %#x\n", glGetError());
+ return false;
+ }
+
+ return createNamedSurfaceTexture(*name, w, h, glConsumer, surface);
+}
+
+void GLHelper::destroySurface(EGLSurface* surface) {
+ if (eglGetCurrentSurface(EGL_READ) == *surface ||
+ eglGetCurrentSurface(EGL_DRAW) == *surface) {
+ eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ }
+ eglDestroySurface(mDisplay, *surface);
+ *surface = EGL_NO_SURFACE;
+}
+
+bool GLHelper::swapBuffers(EGLSurface surface) {
+ EGLint result;
+ result = eglSwapBuffers(mDisplay, surface);
+ if (result != EGL_TRUE) {
+ fprintf(stderr, "eglSwapBuffers error: %#x\n", eglGetError());
+ return false;
+ }
+ return true;
+}
+
+bool GLHelper::getShaderProgram(const char* name, GLuint* outPgm) {
+ for (size_t i = 0; i < mNumShaders; i++) {
+ if (strcmp(mShaderDescs[i].name, name) == 0) {
+ *outPgm = mShaderPrograms[i];
+ return true;
+ }
+ }
+
+ fprintf(stderr, "unknown shader name: \"%s\"\n", name);
+
+ return false;
+}
+
+bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h,
+ sp<GLConsumer>* glConsumer, EGLSurface* surface) {
+ sp<BufferQueue> bq = new BufferQueue(true, mGraphicBufferAlloc);
+ sp<GLConsumer> glc = new GLConsumer(name, true,
+ GL_TEXTURE_EXTERNAL_OES, false, bq);
+ glc->setDefaultBufferSize(w, h);
+ glc->setDefaultMaxBufferCount(3);
+ glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
+
+ sp<ANativeWindow> anw = new SurfaceTextureClient(bq);
+ EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL);
+ if (s == EGL_NO_SURFACE) {
+ fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError());
+ return false;
+ }
+
+ *glConsumer = glc;
+ *surface = s;
+ return true;
+}
+
+bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) {
+ sp<IBinder> dpy = mSurfaceComposerClient->getBuiltInDisplay(0);
+ if (dpy == NULL) {
+ fprintf(stderr, "SurfaceComposer::getBuiltInDisplay failed.\n");
+ return false;
+ }
+
+ DisplayInfo info;
+ status_t err = mSurfaceComposerClient->getDisplayInfo(dpy, &info);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "SurfaceComposer::getDisplayInfo failed: %#x\n", err);
+ return false;
+ }
+
+ float scaleX = float(info.w) / float(w);
+ float scaleY = float(info.h) / float(h);
+ *scale = scaleX < scaleY ? scaleX : scaleY;
+
+ return true;
+}
+
+bool GLHelper::createWindowSurface(uint32_t w, uint32_t h,
+ sp<SurfaceControl>* surfaceControl, EGLSurface* surface) {
+ bool result;
+ status_t err;
+
+ if (mSurfaceComposerClient == NULL) {
+ mSurfaceComposerClient = new SurfaceComposerClient;
+ }
+ err = mSurfaceComposerClient->initCheck();
+ if (err != NO_ERROR) {
+ fprintf(stderr, "SurfaceComposerClient::initCheck error: %#x\n", err);
+ return false;
+ }
+
+ sp<SurfaceControl> sc = mSurfaceComposerClient->createSurface(
+ String8("Benchmark"), w, h, PIXEL_FORMAT_RGBA_8888, 0);
+ if (sc == NULL || !sc->isValid()) {
+ fprintf(stderr, "Failed to create SurfaceControl.\n");
+ return false;
+ }
+
+ float scale;
+ result = computeWindowScale(w, h, &scale);
+ if (!result) {
+ return false;
+ }
+
+ SurfaceComposerClient::openGlobalTransaction();
+ err = sc->setLayer(0x7FFFFFFF);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "SurfaceComposer::setLayer error: %#x\n", err);
+ return false;
+ }
+ err = sc->setMatrix(scale, 0.0f, 0.0f, scale);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "SurfaceComposer::setMatrix error: %#x\n", err);
+ return false;
+ }
+
+ err = sc->show();
+ if (err != NO_ERROR) {
+ fprintf(stderr, "SurfaceComposer::show error: %#x\n", err);
+ return false;
+ }
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ sp<ANativeWindow> anw = sc->getSurface();
+ EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL);
+ if (s == EGL_NO_SURFACE) {
+ fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError());
+ return false;
+ }
+
+ *surfaceControl = sc;
+ *surface = s;
+ return true;
+}
+
+static bool compileShader(GLenum shaderType, const char* src,
+ GLuint* outShader) {
+ GLuint shader = glCreateShader(shaderType);
+ if (shader == 0) {
+ fprintf(stderr, "glCreateShader error: %#x\n", glGetError());
+ return false;
+ }
+
+ glShaderSource(shader, 1, &src, NULL);
+ glCompileShader(shader);
+
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen) {
+ char* buf = new char[infoLen];
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ fprintf(stderr, "Shader compile log:\n%s\n", buf);
+ delete[] buf;
+ }
+ }
+ glDeleteShader(shader);
+ return false;
+ }
+ *outShader = shader;
+ return true;
+}
+
+static void printShaderSource(const char* const* src) {
+ for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) {
+ fprintf(stderr, "%3d: %s\n", i+1, src[i]);
+ }
+}
+
+static const char* makeShaderString(const char* const* src) {
+ size_t len = 0;
+ for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) {
+ // The +1 is for the '\n' that will be added.
+ len += strlen(src[i]) + 1;
+ }
+
+ char* result = new char[len+1];
+ char* end = result;
+ for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) {
+ strcpy(end, src[i]);
+ end += strlen(src[i]);
+ *end = '\n';
+ end++;
+ }
+ *end = '\0';
+
+ return result;
+}
+
+static bool compileShaderLines(GLenum shaderType, const char* const* lines,
+ GLuint* outShader) {
+ const char* src = makeShaderString(lines);
+ bool result = compileShader(shaderType, src, outShader);
+ if (!result) {
+ fprintf(stderr, "Shader source:\n");
+ printShaderSource(lines);
+ return false;
+ }
+ delete[] src;
+
+ return true;
+}
+
+static bool linkShaderProgram(GLuint vs, GLuint fs, GLuint* outPgm) {
+ GLuint program = glCreateProgram();
+ if (program == 0) {
+ fprintf(stderr, "glCreateProgram error: %#x\n", glGetError());
+ return false;
+ }
+
+ glAttachShader(program, vs);
+ glAttachShader(program, fs);
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = new char[bufLength];
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ fprintf(stderr, "Program link log:\n%s\n", buf);
+ delete[] buf;
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+
+ *outPgm = program;
+ return program != 0;
+}
+
+bool GLHelper::setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders) {
+ mShaderPrograms = new GLuint[numShaders];
+ bool result = true;
+
+ for (size_t i = 0; i < numShaders && result; i++) {
+ GLuint vs, fs;
+
+ result = compileShaderLines(GL_VERTEX_SHADER,
+ shaderDescs[i].vertexShader, &vs);
+ if (!result) {
+ return false;
+ }
+
+ result = compileShaderLines(GL_FRAGMENT_SHADER,
+ shaderDescs[i].fragmentShader, &fs);
+ if (!result) {
+ glDeleteShader(vs);
+ return false;
+ }
+
+ result = linkShaderProgram(vs, fs, &mShaderPrograms[i]);
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+ }
+
+ mNumShaders = numShaders;
+ mShaderDescs = shaderDescs;
+
+ return result;
+}
+
+bool GLHelper::getDitherTexture(GLuint* outTexName) {
+ if (mDitherTexture == 0) {
+ const uint8_t pattern[] = {
+ 0, 8, 2, 10,
+ 12, 4, 14, 6,
+ 3, 11, 1, 9,
+ 15, 7, 13, 5
+ };
+
+ glGenTextures(1, &mDitherTexture);
+ glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE,
+ DITHER_KERNEL_SIZE, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
+ }
+
+ *outTexName = mDitherTexture;
+
+ return true;
+}
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2012 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 <gui/GraphicBufferAlloc.h>
+#include <gui/GLConsumer.h>
+#include <gui/SurfaceTextureClient.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+namespace android {
+
+class SurfaceComposerClient;
+class SurfaceControl;
+
+enum { MAX_SHADER_LINES = 128 };
+
+struct ShaderDesc {
+ const char* name;
+ const char* vertexShader[MAX_SHADER_LINES];
+ const char* fragmentShader[MAX_SHADER_LINES];
+};
+
+class GLHelper {
+
+public:
+
+ enum { DITHER_KERNEL_SIZE = 4 };
+
+ GLHelper();
+
+ ~GLHelper();
+
+ bool setUp(const ShaderDesc* shaderDescs, size_t numShaders);
+
+ void tearDown();
+
+ bool makeCurrent(EGLSurface surface);
+
+ bool createSurfaceTexture(uint32_t w, uint32_t h,
+ sp<GLConsumer>* surfaceTexture, EGLSurface* surface,
+ GLuint* name);
+
+ bool createWindowSurface(uint32_t w, uint32_t h,
+ sp<SurfaceControl>* surfaceControl, EGLSurface* surface);
+
+ void destroySurface(EGLSurface* surface);
+
+ bool swapBuffers(EGLSurface surface);
+
+ bool getShaderProgram(const char* name, GLuint* outPgm);
+
+ bool getDitherTexture(GLuint* outTexName);
+
+private:
+
+ bool createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h,
+ sp<GLConsumer>* surfaceTexture, EGLSurface* surface);
+
+ bool computeWindowScale(uint32_t w, uint32_t h, float* scale);
+
+ bool setUpShaders(const ShaderDesc* shaderDescs, size_t numShaders);
+
+ sp<GraphicBufferAlloc> mGraphicBufferAlloc;
+
+ EGLDisplay mDisplay;
+ EGLContext mContext;
+ EGLSurface mDummySurface;
+ sp<GLConsumer> mDummyGLConsumer;
+ EGLConfig mConfig;
+
+ sp<SurfaceComposerClient> mSurfaceComposerClient;
+
+ GLuint* mShaderPrograms;
+ const ShaderDesc* mShaderDescs;
+ size_t mNumShaders;
+
+ GLuint mDitherTexture;
+};
+
+} // namespace android
--- /dev/null
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+
+#include <gui/GraphicBufferAlloc.h>
+#include <gui/Surface.h>
+#include <gui/GLConsumer.h>
+#include <gui/SurfaceTextureClient.h>
+#include <ui/Fence.h>
+#include <utils/Trace.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#include <math.h>
+#include <getopt.h>
+
+#include "Flatland.h"
+#include "GLHelper.h"
+
+using namespace ::android;
+
+static uint32_t g_SleepBetweenSamplesMs = 0;
+static bool g_PresentToWindow = false;
+static size_t g_BenchmarkNameLen = 0;
+
+struct BenchmarkDesc {
+ // The name of the test.
+ const char* name;
+
+ // The dimensions of the space in which window layers are specified.
+ uint32_t width;
+ uint32_t height;
+
+ // The screen heights at which to run the test.
+ uint32_t runHeights[MAX_TEST_RUNS];
+
+ // The list of window layers.
+ LayerDesc layers[MAX_NUM_LAYERS];
+};
+
+static const BenchmarkDesc benchmarks[] = {
+ { "16:10 Single Static Window",
+ 2560, 1600, { 800, 1600, 2400 },
+ {
+ { // Window
+ 0, staticGradient, opaque,
+ 0, 50, 2560, 1454,
+ },
+ { // Status bar
+ 0, staticGradient, opaque,
+ 0, 0, 2560, 50,
+ },
+ { // Navigation bar
+ 0, staticGradient, opaque,
+ 0, 1504, 2560, 96,
+ },
+ },
+ },
+
+ { "16:10 App -> Home Transition",
+ 2560, 1600, { 800, 1600, 2400 },
+ {
+ { // Wallpaper
+ 0, staticGradient, opaque,
+ 0, 50, 2560, 1454,
+ },
+ { // Launcher
+ 0, staticGradient, blend,
+ 0, 50, 2560, 1454,
+ },
+ { // Outgoing activity
+ 0, staticGradient, blendShrink,
+ 20, 70, 2520, 1414,
+ },
+ { // Status bar
+ 0, staticGradient, opaque,
+ 0, 0, 2560, 50,
+ },
+ { // Navigation bar
+ 0, staticGradient, opaque,
+ 0, 1504, 2560, 96,
+ },
+ },
+ },
+
+ { "16:10 SurfaceView -> Home Transition",
+ 2560, 1600, { 800, 1600, 2400 },
+ {
+ { // Wallpaper
+ 0, staticGradient, opaque,
+ 0, 50, 2560, 1454,
+ },
+ { // Launcher
+ 0, staticGradient, blend,
+ 0, 50, 2560, 1454,
+ },
+ { // Outgoing SurfaceView
+ 0, staticGradient, blendShrink,
+ 20, 70, 2520, 1414,
+ },
+ { // Outgoing activity
+ 0, staticGradient, blendShrink,
+ 20, 70, 2520, 1414,
+ },
+ { // Status bar
+ 0, staticGradient, opaque,
+ 0, 0, 2560, 50,
+ },
+ { // Navigation bar
+ 0, staticGradient, opaque,
+ 0, 1504, 2560, 96,
+ },
+ },
+ },
+};
+
+static const ShaderDesc shaders[] = {
+ {
+ name: "Blit",
+ vertexShader: {
+ "precision mediump float;",
+ "",
+ "attribute vec4 position;",
+ "attribute vec4 uv;",
+ "",
+ "varying vec4 texCoords;",
+ "",
+ "uniform mat4 objToNdc;",
+ "uniform mat4 uvToTex;",
+ "",
+ "void main() {",
+ " gl_Position = objToNdc * position;",
+ " texCoords = uvToTex * uv;",
+ "}",
+ },
+ fragmentShader: {
+ "#extension GL_OES_EGL_image_external : require",
+ "precision mediump float;",
+ "",
+ "varying vec4 texCoords;",
+ "",
+ "uniform samplerExternalOES blitSrc;",
+ "uniform vec4 modColor;",
+ "",
+ "void main() {",
+ " gl_FragColor = texture2D(blitSrc, texCoords.xy);",
+ " gl_FragColor *= modColor;",
+ "}",
+ },
+ },
+
+ {
+ name: "Gradient",
+ vertexShader: {
+ "precision mediump float;",
+ "",
+ "attribute vec4 position;",
+ "attribute vec4 uv;",
+ "",
+ "varying float interp;",
+ "",
+ "uniform mat4 objToNdc;",
+ "uniform mat4 uvToInterp;",
+ "",
+ "void main() {",
+ " gl_Position = objToNdc * position;",
+ " interp = (uvToInterp * uv).x;",
+ "}",
+ },
+ fragmentShader: {
+ "precision mediump float;",
+ "",
+ "varying float interp;",
+ "",
+ "uniform vec4 color0;",
+ "uniform vec4 color1;",
+ "",
+ "uniform sampler2D ditherKernel;",
+ "uniform float invDitherKernelSize;",
+ "uniform float invDitherKernelSizeSq;",
+ "",
+ "void main() {",
+ " float dither = texture2D(ditherKernel,",
+ " gl_FragCoord.xy * invDitherKernelSize).a;",
+ " dither *= invDitherKernelSizeSq;",
+ " vec4 color = mix(color0, color1, clamp(interp, 0.0, 1.0));",
+ " gl_FragColor = color + vec4(dither, dither, dither, 0.0);",
+ "}",
+ },
+ },
+};
+
+class Layer {
+
+public:
+
+ Layer() :
+ mFirstFrame(true),
+ mGLHelper(NULL),
+ mSurface(EGL_NO_SURFACE) {
+ }
+
+ bool setUp(const LayerDesc& desc, GLHelper* helper) {
+ bool result;
+
+ mDesc = desc;
+ mGLHelper = helper;
+
+ result = mGLHelper->createSurfaceTexture(mDesc.width, mDesc.height,
+ &mGLConsumer, &mSurface, &mTexName);
+ if (!result) {
+ return false;
+ }
+
+ mRenderer = desc.rendererFactory();
+ result = mRenderer->setUp(helper);
+ if (!result) {
+ return false;
+ }
+
+ mComposer = desc.composerFactory();
+ result = mComposer->setUp(desc, helper);
+ if (!result) {
+ return false;
+ }
+
+ return true;
+ }
+
+ void tearDown() {
+ if (mComposer != NULL) {
+ mComposer->tearDown();
+ delete mComposer;
+ mComposer = NULL;
+ }
+
+ if (mRenderer != NULL) {
+ mRenderer->tearDown();
+ delete mRenderer;
+ mRenderer = NULL;
+ }
+
+ if (mSurface != EGL_NO_SURFACE) {
+ mGLHelper->destroySurface(&mSurface);
+ mGLConsumer->abandon();
+ }
+ mGLHelper = NULL;
+ mGLConsumer.clear();
+ }
+
+ bool render() {
+ return mRenderer->render(mSurface);
+ }
+
+ bool prepareComposition() {
+ status_t err;
+
+ err = mGLConsumer->updateTexImage();
+ if (err < 0) {
+ fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err);
+ return false;
+ }
+
+ return true;
+ }
+
+ bool compose() {
+ return mComposer->compose(mTexName, mGLConsumer);
+ }
+
+private:
+ bool mFirstFrame;
+
+ LayerDesc mDesc;
+
+ GLHelper* mGLHelper;
+
+ GLuint mTexName;
+ sp<GLConsumer> mGLConsumer;
+ EGLSurface mSurface;
+
+ Renderer* mRenderer;
+ Composer* mComposer;
+};
+
+class BenchmarkRunner {
+
+public:
+
+ BenchmarkRunner(const BenchmarkDesc& desc, size_t instance) :
+ mDesc(desc),
+ mInstance(instance),
+ mNumLayers(countLayers(desc)),
+ mGLHelper(NULL),
+ mSurface(EGL_NO_SURFACE),
+ mWindowSurface(EGL_NO_SURFACE) {
+ }
+
+ bool setUp() {
+ ATRACE_CALL();
+
+ bool result;
+ EGLint resulte;
+
+ float scaleFactor = float(mDesc.runHeights[mInstance]) /
+ float(mDesc.height);
+ uint32_t w = uint32_t(scaleFactor * float(mDesc.width));
+ uint32_t h = mDesc.runHeights[mInstance];
+
+ mGLHelper = new GLHelper();
+ result = mGLHelper->setUp(shaders, NELEMS(shaders));
+ if (!result) {
+ return false;
+ }
+
+ GLuint texName;
+ result = mGLHelper->createSurfaceTexture(w, h, &mGLConsumer, &mSurface,
+ &texName);
+ if (!result) {
+ return false;
+ }
+
+ for (size_t i = 0; i < mNumLayers; i++) {
+ // Scale the layer to match the current screen size.
+ LayerDesc ld = mDesc.layers[i];
+ ld.x = int32_t(scaleFactor * float(ld.x));
+ ld.y = int32_t(scaleFactor * float(ld.y));
+ ld.width = uint32_t(scaleFactor * float(ld.width));
+ ld.height = uint32_t(scaleFactor * float(ld.height));
+
+ // Set up the layer.
+ result = mLayers[i].setUp(ld, mGLHelper);
+ if (!result) {
+ return false;
+ }
+ }
+
+ if (g_PresentToWindow) {
+ result = mGLHelper->createWindowSurface(w, h, &mSurfaceControl,
+ &mWindowSurface);
+ if (!result) {
+ return false;
+ }
+
+ result = doFrame(mWindowSurface);
+ if (!result) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void tearDown() {
+ ATRACE_CALL();
+
+ for (size_t i = 0; i < mNumLayers; i++) {
+ mLayers[i].tearDown();
+ }
+
+ if (mGLHelper != NULL) {
+ if (mWindowSurface != EGL_NO_SURFACE) {
+ mGLHelper->destroySurface(&mWindowSurface);
+ }
+ mGLHelper->destroySurface(&mSurface);
+ mGLConsumer->abandon();
+ mGLConsumer.clear();
+ mSurfaceControl.clear();
+ mGLHelper->tearDown();
+ delete mGLHelper;
+ mGLHelper = NULL;
+ }
+ }
+
+ nsecs_t run(uint32_t warmUpFrames, uint32_t totalFrames) {
+ ATRACE_CALL();
+
+ bool result;
+ status_t err;
+
+ resetColorGenerator();
+
+ // Do the warm-up frames.
+ for (uint32_t i = 0; i < warmUpFrames; i++) {
+ result = doFrame(mSurface);
+ if (!result) {
+ return -1;
+ }
+ }
+
+ // Grab the fence for the start timestamp.
+ sp<Fence> startFence = mGLConsumer->getCurrentFence();
+
+ // the timed frames.
+ for (uint32_t i = warmUpFrames; i < totalFrames; i++) {
+ result = doFrame(mSurface);
+ if (!result) {
+ return -1;
+ }
+ }
+
+ // Grab the fence for the end timestamp.
+ sp<Fence> endFence = mGLConsumer->getCurrentFence();
+
+ // Keep doing frames until the end fence has signaled.
+ while (endFence->wait(0) == -ETIME) {
+ result = doFrame(mSurface);
+ if (!result) {
+ return -1;
+ }
+ }
+
+ // Compute the time delta.
+ nsecs_t startTime = startFence->getSignalTime();
+ nsecs_t endTime = endFence->getSignalTime();
+
+ return endTime - startTime;
+ }
+
+private:
+
+ bool doFrame(EGLSurface surface) {
+ bool result;
+ status_t err;
+
+ for (size_t i = 0; i < mNumLayers; i++) {
+ result = mLayers[i].render();
+ if (!result) {
+ return false;
+ }
+ }
+
+ for (size_t i = 0; i < mNumLayers; i++) {
+ result = mLayers[i].prepareComposition();
+ if (!result) {
+ return false;
+ }
+ }
+
+ result = mGLHelper->makeCurrent(surface);
+ if (!result) {
+ return false;
+ }
+
+ glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ for (size_t i = 0; i < mNumLayers; i++) {
+ result = mLayers[i].compose();
+ if (!result) {
+ return false;
+ }
+ }
+
+ result = mGLHelper->swapBuffers(surface);
+ if (!result) {
+ return false;
+ }
+
+ err = mGLConsumer->updateTexImage();
+ if (err < 0) {
+ fprintf(stderr, "GLConsumer::updateTexImage error: %d\n", err);
+ return false;
+ }
+
+ return true;
+ }
+
+ static size_t countLayers(const BenchmarkDesc& desc) {
+ size_t i;
+ for (i = 0; i < MAX_NUM_LAYERS; i++) {
+ if (desc.layers[i].rendererFactory == NULL) {
+ break;
+ }
+ }
+ return i;
+ }
+
+ const BenchmarkDesc& mDesc;
+ const size_t mInstance;
+ const size_t mNumLayers;
+
+ GLHelper* mGLHelper;
+
+ // The surface into which layers are composited
+ sp<GLConsumer> mGLConsumer;
+ EGLSurface mSurface;
+
+ // Used for displaying the surface to a window.
+ EGLSurface mWindowSurface;
+ sp<SurfaceControl> mSurfaceControl;
+
+ Layer mLayers[MAX_NUM_LAYERS];
+};
+
+static int cmpDouble(const double* lhs, const double* rhs) {
+ if (*lhs < *rhs) {
+ return -1;
+ } else if (*rhs < *lhs) {
+ return 1;
+ }
+ return 0;
+}
+
+// Run a single benchmark and print the result.
+static bool runTest(const BenchmarkDesc b, size_t run) {
+ bool success = true;
+ double prevResult = 0.0, result = 0.0;
+ Vector<double> samples;
+
+ uint32_t runHeight = b.runHeights[run];
+ uint32_t runWidth = b.width * runHeight / b.height;
+ printf(" %-*s | %4d x %4d | ", g_BenchmarkNameLen, b.name,
+ runWidth, runHeight);
+ fflush(stdout);
+
+ BenchmarkRunner r(b, run);
+ if (!r.setUp()) {
+ fprintf(stderr, "error initializing runner.\n");
+ return false;
+ }
+
+ // The slowest 1/outlierFraction sample results are ignored as potential
+ // outliers.
+ const uint32_t outlierFraction = 16;
+ const double threshold = .0025;
+
+ uint32_t warmUpFrames = 1;
+ uint32_t totalFrames = 5;
+
+ // Find the number of frames needed to run for over 100ms.
+ double runTime = 0.0;
+ while (true) {
+ runTime = double(r.run(warmUpFrames, totalFrames));
+ if (runTime < 50e6) {
+ warmUpFrames *= 2;
+ totalFrames *= 2;
+ } else {
+ break;
+ }
+ }
+
+
+ if (totalFrames - warmUpFrames > 16) {
+ // The test runs too fast to get a stable result. Skip it.
+ printf(" fast");
+ goto done;
+ } else if (totalFrames == 5 && runTime > 200e6) {
+ // The test runs too slow to be very useful. Skip it.
+ printf(" slow");
+ goto done;
+ }
+
+ do {
+ size_t newSamples = samples.size();
+ if (newSamples == 0) {
+ newSamples = 4*outlierFraction;
+ }
+
+ if (newSamples > 512) {
+ printf("varies");
+ goto done;
+ }
+
+ for (size_t i = 0; i < newSamples; i++) {
+ double sample = double(r.run(warmUpFrames, totalFrames));
+
+ if (g_SleepBetweenSamplesMs > 0) {
+ usleep(g_SleepBetweenSamplesMs * 1000);
+ }
+
+ if (sample < 0.0) {
+ success = false;
+ goto done;
+ }
+
+ samples.add(sample);
+ }
+
+ samples.sort(cmpDouble);
+
+ prevResult = result;
+ size_t elem = (samples.size() * (outlierFraction-1) / outlierFraction);
+ result = (samples[elem-1] + samples[elem]) * 0.5;
+ } while (fabs(result - prevResult) > threshold * result);
+
+ printf("%6.3f", result / double(totalFrames - warmUpFrames) / 1e6);
+
+done:
+
+ printf("\n");
+ fflush(stdout);
+ r.tearDown();
+
+ return success;
+}
+
+static void printResultsTableHeader() {
+ const char* scenario = "Scenario";
+ size_t len = strlen(scenario);
+ size_t leftPad = (g_BenchmarkNameLen - len) / 2;
+ size_t rightPad = g_BenchmarkNameLen - len - leftPad;
+ printf(" %*s%s%*s | Resolution | Time (ms)\n", leftPad, "",
+ "Scenario", rightPad, "");
+}
+
+// Run ALL the benchmarks!
+static bool runTests() {
+ printResultsTableHeader();
+
+ for (size_t i = 0; i < NELEMS(benchmarks); i++) {
+ const BenchmarkDesc& b = benchmarks[i];
+ for (size_t j = 0; j < MAX_TEST_RUNS && b.runHeights[j]; j++) {
+ if (!runTest(b, j)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Return the length longest benchmark name.
+static size_t maxBenchmarkNameLen() {
+ size_t maxLen = 0;
+ for (size_t i = 0; i < NELEMS(benchmarks); i++) {
+ const BenchmarkDesc& b = benchmarks[i];
+ size_t len = strlen(b.name);
+ if (len > maxLen) {
+ maxLen = len;
+ }
+ }
+ return maxLen;
+}
+
+// Print the command usage help to stderr.
+static void showHelp(const char *cmd) {
+ fprintf(stderr, "usage: %s [options]\n", cmd);
+ fprintf(stderr, "options include:\n"
+ " -s N sleep for N ms between samples\n"
+ " -d display the test frame to a window\n"
+ " --help print this helpful message and exit\n"
+ );
+}
+
+int main(int argc, char** argv) {
+ if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+ showHelp(argv[0]);
+ exit(0);
+ }
+
+ for (;;) {
+ int ret;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 0 },
+ { 0, 0, 0, 0 }
+ };
+
+ ret = getopt_long(argc, argv, "ds:",
+ long_options, &option_index);
+
+ if (ret < 0) {
+ break;
+ }
+
+ switch(ret) {
+ case 'd':
+ g_PresentToWindow = true;
+ break;
+
+ case 's':
+ g_SleepBetweenSamplesMs = atoi(optarg);
+ break;
+
+ case 0:
+ if (strcmp(long_options[option_index].name, "help")) {
+ showHelp(argv[0]);
+ exit(0);
+ }
+ break;
+
+ default:
+ showHelp(argv[0]);
+ exit(2);
+ }
+ }
+
+ g_BenchmarkNameLen = maxBenchmarkNameLen();
+
+ printf(" cmdline:");
+ for (int i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\n");
+
+ if (!runTests()) {
+ fprintf(stderr, "exiting due to error.\n");
+ return 1;
+ }
+}
--- /dev/null
+Flatland is a benchmark for measuring GPU performance in various 2D UI
+rendering and window compositing scenarios. It is designed to be used early
+in the device development process to evaluate GPU hardware (e.g. for SoC
+selection). It uses OpenGL ES 2.0, gralloc, and the Android explicit
+synchronization framework, so it can only be run on devices with drivers
+supporting those HALs.
+
+
+Preparing a Device
+
+Because it's measuring hardware performance, flatland should be run in as
+consistent and static an environment as possible. The display should be
+turned off and background services should be stopped before running the
+benchmark. Running 'adb shell stop' after turning off the display is probably
+sufficient for this, but if there are device- specific background services
+that consume much CPU cycles, memory bandwidth, or might otherwise interfere
+with GPU rendering, those should be stopped as well (and ideally they'd be
+fixed or eliminated for production devices).
+
+Additionally, all relevant hardware clocks should be locked at a particular
+frequency when running flatland. At a minimum this includes the CPU, GPU, and
+memory bus clocks. Running flatland with dynamic clocking essentially
+measures the behavior of the dynamic clocking algorithm under a fairly
+unrealistic workload, and will likely result in unstable and useless results.
+
+If running the benchmark with the clocks locked causes thermal issues, the -s
+command line option can be used to insert a sleep (specified in milliseconds)
+in between each benchmark sample run. Regardless of the scenario being
+measured, each sample measurement runs for between 50 and 200 ms, so a sleep
+time between 10 and 50 ms should address most thermal problems.
+
+
+Interpreting the Output
+
+The output of flatland should look something like this:
+
+ cmdline: flatland
+ Scenario | Resolution | Time (ms)
+ 16:10 Single Static Window | 1280 x 800 | fast
+ 16:10 Single Static Window | 2560 x 1600 | 5.368
+ 16:10 Single Static Window | 3840 x 2400 | 11.979
+ 16:10 App -> Home Transition | 1280 x 800 | 4.069
+ 16:10 App -> Home Transition | 2560 x 1600 | 15.911
+ 16:10 App -> Home Transition | 3840 x 2400 | 38.795
+ 16:10 SurfaceView -> Home Transition | 1280 x 800 | 5.387
+ 16:10 SurfaceView -> Home Transition | 2560 x 1600 | 21.147
+ 16:10 SurfaceView -> Home Transition | 3840 x 2400 | slow
+
+The first column is simply a description of the scenario that's being
+simulated. The second column indicates the resolution at which the scenario
+was measured. The third column is the measured benchmark result. It
+indicates the expected time in milliseconds that a single frame of the
+scenario takes to complete.
+
+The third column may also contain one of three other values:
+
+ fast - This indicates that frames of the scenario completed too fast to be
+ reliably benchmarked. This corresponds to a frame time less than 3 ms.
+ Rather than spending time trying (and likely failing) to get a stable
+ result, the scenario was skipped.
+
+ slow - This indicates that frames of the scenario took too long to
+ complete. This corresponds to a frame time over 50 ms. Rather than
+ simulating a scenario that is obviously impractical on this device, the
+ scenario was skipped.
+
+ varies - This indicates that the scenario was measured, but it did not
+ yield a stable result. Occasionally this happens with an otherwise stable
+ scenario. In this case, simply rerunning flatland should yield a valid
+ result. If a scenario repeatedly results in a 'varies' output, that
+ probably indicates that something is wrong with the environment in which
+ flatland is being run. Check that the hardware clock frequencies are
+ locked and that no heavy-weight services / daemons are running in the
+ background.
--- /dev/null
+/*
+ * Copyright (C) 2012 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 "Flatland.h"
+#include "GLHelper.h"
+
+namespace android {
+
+static float colors[][4] = {
+ { .85f, .14f, .44f, 1.0f },
+ { .91f, .72f, .10f, 1.0f },
+ { .04f, .66f, .42f, 1.0f },
+ { .84f, .39f, .68f, 1.0f },
+ { .38f, .53f, .78f, 1.0f },
+};
+
+static size_t g_colorIndex;
+
+const float* genColor() {
+ float* color = colors[g_colorIndex];
+ g_colorIndex = (g_colorIndex + 1) % NELEMS(colors);
+ return color;
+}
+
+void resetColorGenerator() {
+ g_colorIndex = 0;
+}
+
+class GradientRenderer {
+
+public:
+
+ bool setUp(GLHelper* helper) {
+ bool result;
+
+ result = helper->getShaderProgram("Gradient", &mGradPgm);
+ if (!result) {
+ return false;
+ }
+
+ result = helper->getDitherTexture(&mDitherTexName);
+ if (!result) {
+ return false;
+ }
+
+ mPosAttribLoc = glGetAttribLocation(mGradPgm, "position");
+ mUVAttribLoc = glGetAttribLocation(mGradPgm, "uv");
+ mUVToInterpUniformLoc = glGetUniformLocation(mGradPgm, "uvToInterp");
+ mObjToNdcUniformLoc = glGetUniformLocation(mGradPgm, "objToNdc");
+ mDitherKernelSamplerLoc = glGetUniformLocation(mGradPgm, "ditherKernel");
+ mInvDitherKernelSizeUniformLoc = glGetUniformLocation(mGradPgm,
+ "invDitherKernelSize");
+ mInvDitherKernelSizeSqUniformLoc = glGetUniformLocation(mGradPgm,
+ "invDitherKernelSizeSq");
+ mColor0UniformLoc = glGetUniformLocation(mGradPgm, "color0");
+ mColor1UniformLoc = glGetUniformLocation(mGradPgm, "color1");
+
+ return true;
+ }
+
+ void tearDown() {
+ }
+
+ bool drawGradient() {
+ float identity[16] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f,
+ };
+ const float pos[] = {
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f,
+ };
+ const float uv[] = {
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ };
+ const float* color0 = genColor();
+ const float* color1 = genColor();
+
+ glUseProgram(mGradPgm);
+
+ glVertexAttribPointer(mPosAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, pos);
+ glVertexAttribPointer(mUVAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, uv);
+ glEnableVertexAttribArray(mPosAttribLoc);
+ glEnableVertexAttribArray(mUVAttribLoc);
+
+ float invDitherKernelSize = 1.0f / float(GLHelper::DITHER_KERNEL_SIZE);
+ float invDitherKernelSizeSq = invDitherKernelSize * invDitherKernelSize;
+
+ glUniformMatrix4fv(mObjToNdcUniformLoc, 1, GL_FALSE, identity);
+ glUniformMatrix4fv(mUVToInterpUniformLoc, 1, GL_FALSE, identity);
+ glUniform1f(mInvDitherKernelSizeUniformLoc, invDitherKernelSize);
+ glUniform1f(mInvDitherKernelSizeSqUniformLoc, invDitherKernelSizeSq);
+ glUniform4fv(mColor0UniformLoc, 1, color0);
+ glUniform4fv(mColor1UniformLoc, 1, color1);
+
+ if (glGetError() != GL_NO_ERROR) {
+ fprintf(stderr, "GL error! 0\n");
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, mDitherTexName);
+
+ if (glGetError() != GL_NO_ERROR) {
+ fprintf(stderr, "GL error! 1\n");
+ }
+
+ glUniform1i(mDitherKernelSamplerLoc, 0);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glDisableVertexAttribArray(mPosAttribLoc);
+ glDisableVertexAttribArray(mUVAttribLoc);
+
+ if (glGetError() != GL_NO_ERROR) {
+ fprintf(stderr, "GL error! 2\n");
+ }
+
+ return true;
+ }
+
+ GLuint mGradPgm;
+ GLuint mDitherTexName;
+ GLuint mPosAttribLoc;
+ GLuint mUVAttribLoc;
+ GLuint mObjToNdcUniformLoc;
+ GLuint mUVToInterpUniformLoc;
+ GLuint mDitherKernelSamplerLoc;
+ GLuint mInvDitherKernelSizeUniformLoc;
+ GLuint mInvDitherKernelSizeSqUniformLoc;
+ GLuint mColor0UniformLoc;
+ GLuint mColor1UniformLoc;
+};
+
+Renderer* staticGradient() {
+ class NoRenderer : public Renderer {
+ virtual bool setUp(GLHelper* helper) {
+ mIsFirstFrame = true;
+ mGLHelper = helper;
+ return mGradientRenderer.setUp(helper);
+ }
+
+ virtual void tearDown() {
+ mGradientRenderer.tearDown();
+ }
+
+ virtual bool render(EGLSurface surface) {
+ if (mIsFirstFrame) {
+ bool result;
+ mIsFirstFrame = false;
+
+ result = mGLHelper->makeCurrent(surface);
+ if (!result) {
+ return false;
+ }
+
+ result = mGradientRenderer.drawGradient();
+ if (!result) {
+ return false;
+ }
+
+ result = mGLHelper->swapBuffers(surface);
+ if (!result) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool mIsFirstFrame;
+ GLHelper* mGLHelper;
+ GradientRenderer mGradientRenderer;
+ };
+ return new NoRenderer;
+}
+
+
+} // namespace android
--- /dev/null
+LOCAL_PATH := $(call my-dir)
+
+common_src_files := \
+ commands.c utils.c
+
+#
+# Static library used in testing and executable
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ $(common_src_files)
+
+LOCAL_MODULE := libinstalld
+
+LOCAL_MODULE_TAGS := eng tests
+
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# Executable
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ installd.c \
+ $(common_src_files)
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libselinux
+
+LOCAL_STATIC_LIBRARIES := \
+ libdiskusage
+
+LOCAL_MODULE := installd
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
--- /dev/null
+/*
+** Copyright 2008, 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 <linux/capability.h>
+#include "installd.h"
+#include <diskusage/dirsize.h>
+#include <selinux/android.h>
+
+/* Directory records that are used in execution of commands. */
+dir_rec_t android_data_dir;
+dir_rec_t android_asec_dir;
+dir_rec_t android_app_dir;
+dir_rec_t android_app_private_dir;
+dir_rec_t android_app_lib_dir;
+dir_rec_t android_media_dir;
+dir_rec_array_t android_system_dirs;
+
+int install(const char *pkgname, uid_t uid, gid_t gid)
+{
+ char pkgdir[PKG_PATH_MAX];
+ char libsymlink[PKG_PATH_MAX];
+ char applibdir[PKG_PATH_MAX];
+ struct stat libStat;
+
+ if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
+ ALOGE("invalid uid/gid: %d %d\n", uid, gid);
+ return -1;
+ }
+
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
+ ALOGE("cannot create package path\n");
+ return -1;
+ }
+
+ if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, 0)) {
+ ALOGE("cannot create package lib symlink origin path\n");
+ return -1;
+ }
+
+ if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {
+ ALOGE("cannot create package lib symlink dest path\n");
+ return -1;
+ }
+
+ if (mkdir(pkgdir, 0751) < 0) {
+ ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
+ return -1;
+ }
+ if (chmod(pkgdir, 0751) < 0) {
+ ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(pkgdir);
+ return -1;
+ }
+
+ if (lstat(libsymlink, &libStat) < 0) {
+ if (errno != ENOENT) {
+ ALOGE("couldn't stat lib dir: %s\n", strerror(errno));
+ return -1;
+ }
+ } else {
+ if (S_ISDIR(libStat.st_mode)) {
+ if (delete_dir_contents(libsymlink, 1, 0) < 0) {
+ ALOGE("couldn't delete lib directory during install for: %s", libsymlink);
+ return -1;
+ }
+ } else if (S_ISLNK(libStat.st_mode)) {
+ if (unlink(libsymlink) < 0) {
+ ALOGE("couldn't unlink lib directory during install for: %s", libsymlink);
+ return -1;
+ }
+ }
+ }
+
+ if (symlink(applibdir, libsymlink) < 0) {
+ ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, applibdir,
+ strerror(errno));
+ unlink(pkgdir);
+ return -1;
+ }
+
+ if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) {
+ ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(libsymlink);
+ unlink(pkgdir);
+ return -errno;
+ }
+
+ if (chown(pkgdir, uid, gid) < 0) {
+ ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(libsymlink);
+ unlink(pkgdir);
+ return -1;
+ }
+
+ return 0;
+}
+
+int uninstall(const char *pkgname, uid_t persona)
+{
+ char pkgdir[PKG_PATH_MAX];
+
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona))
+ return -1;
+
+ /* delete contents AND directory, no exceptions */
+ return delete_dir_contents(pkgdir, 1, NULL);
+}
+
+int renamepkg(const char *oldpkgname, const char *newpkgname)
+{
+ char oldpkgdir[PKG_PATH_MAX];
+ char newpkgdir[PKG_PATH_MAX];
+
+ if (create_pkg_path(oldpkgdir, oldpkgname, PKG_DIR_POSTFIX, 0))
+ return -1;
+ if (create_pkg_path(newpkgdir, newpkgname, PKG_DIR_POSTFIX, 0))
+ return -1;
+
+ if (rename(oldpkgdir, newpkgdir) < 0) {
+ ALOGE("cannot rename dir '%s' to '%s': %s\n", oldpkgdir, newpkgdir, strerror(errno));
+ return -errno;
+ }
+ return 0;
+}
+
+int fix_uid(const char *pkgname, uid_t uid, gid_t gid)
+{
+ char pkgdir[PKG_PATH_MAX];
+ struct stat s;
+ int rc = 0;
+
+ if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
+ ALOGE("invalid uid/gid: %d %d\n", uid, gid);
+ return -1;
+ }
+
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
+ ALOGE("cannot create package path\n");
+ return -1;
+ }
+
+ if (stat(pkgdir, &s) < 0) return -1;
+
+ if (s.st_uid != 0 || s.st_gid != 0) {
+ ALOGE("fixing uid of non-root pkg: %s %lu %lu\n", pkgdir, s.st_uid, s.st_gid);
+ return -1;
+ }
+
+ if (chmod(pkgdir, 0751) < 0) {
+ ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(pkgdir);
+ return -errno;
+ }
+ if (chown(pkgdir, uid, gid) < 0) {
+ ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(pkgdir);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int delete_user_data(const char *pkgname, uid_t persona)
+{
+ char pkgdir[PKG_PATH_MAX];
+
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona))
+ return -1;
+
+ /* delete contents, excluding "lib", but not the directory itself */
+ return delete_dir_contents(pkgdir, 0, "lib");
+}
+
+int make_user_data(const char *pkgname, uid_t uid, uid_t persona)
+{
+ char pkgdir[PKG_PATH_MAX];
+ char applibdir[PKG_PATH_MAX];
+ char libsymlink[PKG_PATH_MAX];
+ struct stat libStat;
+
+ // Create the data dir for the package
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) {
+ return -1;
+ }
+ if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, persona)) {
+ ALOGE("cannot create package lib symlink origin path\n");
+ return -1;
+ }
+ if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {
+ ALOGE("cannot create package lib symlink dest path\n");
+ return -1;
+ }
+
+ if (mkdir(pkgdir, 0751) < 0) {
+ ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
+ return -errno;
+ }
+ if (chmod(pkgdir, 0751) < 0) {
+ ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(pkgdir);
+ return -errno;
+ }
+
+ if (lstat(libsymlink, &libStat) < 0) {
+ if (errno != ENOENT) {
+ ALOGE("couldn't stat lib dir for non-primary: %s\n", strerror(errno));
+ unlink(pkgdir);
+ return -1;
+ }
+ } else {
+ if (S_ISDIR(libStat.st_mode)) {
+ if (delete_dir_contents(libsymlink, 1, 0) < 0) {
+ ALOGE("couldn't delete lib directory during install for non-primary: %s",
+ libsymlink);
+ unlink(pkgdir);
+ return -1;
+ }
+ } else if (S_ISLNK(libStat.st_mode)) {
+ if (unlink(libsymlink) < 0) {
+ ALOGE("couldn't unlink lib directory during install for non-primary: %s",
+ libsymlink);
+ unlink(pkgdir);
+ return -1;
+ }
+ }
+ }
+
+ if (symlink(applibdir, libsymlink) < 0) {
+ ALOGE("couldn't symlink directory for non-primary '%s' -> '%s': %s\n", libsymlink,
+ applibdir, strerror(errno));
+ unlink(pkgdir);
+ return -1;
+ }
+
+ if (selinux_android_setfilecon(pkgdir, pkgname, uid) < 0) {
+ ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(libsymlink);
+ unlink(pkgdir);
+ return -errno;
+ }
+
+ if (chown(pkgdir, uid, uid) < 0) {
+ ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(libsymlink);
+ unlink(pkgdir);
+ return -errno;
+ }
+
+ return 0;
+}
+
+int delete_persona(uid_t persona)
+{
+ char data_path[PKG_PATH_MAX];
+ if (create_persona_path(data_path, persona)) {
+ return -1;
+ }
+ if (delete_dir_contents(data_path, 1, NULL)) {
+ return -1;
+ }
+
+ char media_path[PATH_MAX];
+ if (create_persona_media_path(media_path, (userid_t) persona) == -1) {
+ return -1;
+ }
+ if (delete_dir_contents(media_path, 1, NULL) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int delete_cache(const char *pkgname, uid_t persona)
+{
+ char cachedir[PKG_PATH_MAX];
+
+ if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, persona))
+ return -1;
+
+ /* delete contents, not the directory, no exceptions */
+ return delete_dir_contents(cachedir, 0, 0);
+}
+
+/* Try to ensure free_size bytes of storage are available.
+ * Returns 0 on success.
+ * This is rather simple-minded because doing a full LRU would
+ * be potentially memory-intensive, and without atime it would
+ * also require that apps constantly modify file metadata even
+ * when just reading from the cache, which is pretty awful.
+ */
+int free_cache(int64_t free_size)
+{
+ cache_t* cache;
+ int64_t avail;
+ DIR *d;
+ struct dirent *de;
+ char tmpdir[PATH_MAX];
+ char *dirpos;
+
+ avail = data_disk_free();
+ if (avail < 0) return -1;
+
+ ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail);
+ if (avail >= free_size) return 0;
+
+ cache = start_cache_collection();
+
+ // Collect cache files for primary user.
+ if (create_persona_path(tmpdir, 0) == 0) {
+ //ALOGI("adding cache files from %s\n", tmpdir);
+ add_cache_files(cache, tmpdir, "cache");
+ }
+
+ // Search for other users and add any cache files from them.
+ snprintf(tmpdir, sizeof(tmpdir), "%s%s", android_data_dir.path,
+ SECONDARY_USER_PREFIX);
+ dirpos = tmpdir + strlen(tmpdir);
+ d = opendir(tmpdir);
+ if (d != NULL) {
+ while ((de = readdir(d))) {
+ if (de->d_type == DT_DIR) {
+ const char *name = de->d_name;
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+ if ((strlen(name)+(dirpos-tmpdir)) < (sizeof(tmpdir)-1)) {
+ strcpy(dirpos, name);
+ //ALOGI("adding cache files from %s\n", tmpdir);
+ add_cache_files(cache, tmpdir, "cache");
+ } else {
+ ALOGW("Path exceeds limit: %s%s", tmpdir, name);
+ }
+ }
+ }
+ closedir(d);
+ }
+
+ // Collect cache files on external storage for all users (if it is mounted as part
+ // of the internal storage).
+ strcpy(tmpdir, android_media_dir.path);
+ dirpos = tmpdir + strlen(tmpdir);
+ d = opendir(tmpdir);
+ if (d != NULL) {
+ while ((de = readdir(d))) {
+ if (de->d_type == DT_DIR) {
+ const char *name = de->d_name;
+ /* skip any dir that doesn't start with a number, so not a user */
+ if (name[0] < '0' || name[0] > '9') {
+ continue;
+ }
+ if ((strlen(name)+(dirpos-tmpdir)) < (sizeof(tmpdir)-1)) {
+ strcpy(dirpos, name);
+ if (lookup_media_dir(tmpdir, "Android") == 0
+ && lookup_media_dir(tmpdir, "data") == 0) {
+ //ALOGI("adding cache files from %s\n", tmpdir);
+ add_cache_files(cache, tmpdir, "cache");
+ }
+ } else {
+ ALOGW("Path exceeds limit: %s%s", tmpdir, name);
+ }
+ }
+ }
+ closedir(d);
+ }
+
+ clear_cache_files(cache, free_size);
+ finish_cache_collection(cache);
+
+ return data_disk_free() >= free_size ? 0 : -1;
+}
+
+int move_dex(const char *src, const char *dst)
+{
+ char src_dex[PKG_PATH_MAX];
+ char dst_dex[PKG_PATH_MAX];
+
+ if (validate_apk_path(src)) return -1;
+ if (validate_apk_path(dst)) return -1;
+
+ if (create_cache_path(src_dex, src)) return -1;
+ if (create_cache_path(dst_dex, dst)) return -1;
+
+ ALOGV("move %s -> %s\n", src_dex, dst_dex);
+ if (rename(src_dex, dst_dex) < 0) {
+ ALOGE("Couldn't move %s: %s\n", src_dex, strerror(errno));
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+int rm_dex(const char *path)
+{
+ char dex_path[PKG_PATH_MAX];
+
+ if (validate_apk_path(path)) return -1;
+ if (create_cache_path(dex_path, path)) return -1;
+
+ ALOGV("unlink %s\n", dex_path);
+ if (unlink(dex_path) < 0) {
+ ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno));
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+int get_size(const char *pkgname, int persona, const char *apkpath,
+ const char *fwdlock_apkpath, const char *asecpath,
+ int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize,
+ int64_t* _asecsize)
+{
+ DIR *d;
+ int dfd;
+ struct dirent *de;
+ struct stat s;
+ char path[PKG_PATH_MAX];
+
+ int64_t codesize = 0;
+ int64_t datasize = 0;
+ int64_t cachesize = 0;
+ int64_t asecsize = 0;
+
+ /* count the source apk as code -- but only if it's not
+ * on the /system partition and its not on the sdcard.
+ */
+ if (validate_system_app_path(apkpath) &&
+ strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) {
+ if (stat(apkpath, &s) == 0) {
+ codesize += stat_size(&s);
+ }
+ }
+ /* count the forward locked apk as code if it is given
+ */
+ if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') {
+ if (stat(fwdlock_apkpath, &s) == 0) {
+ codesize += stat_size(&s);
+ }
+ }
+ /* count the cached dexfile as code */
+ if (!create_cache_path(path, apkpath)) {
+ if (stat(path, &s) == 0) {
+ codesize += stat_size(&s);
+ }
+ }
+
+ /* add in size of any libraries */
+ if (!create_pkg_path_in_dir(path, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {
+ d = opendir(path);
+ if (d != NULL) {
+ dfd = dirfd(d);
+ codesize += calculate_dir_size(dfd);
+ closedir(d);
+ }
+ }
+
+ /* compute asec size if it is given
+ */
+ if (asecpath != NULL && asecpath[0] != '!') {
+ if (stat(asecpath, &s) == 0) {
+ asecsize += stat_size(&s);
+ }
+ }
+
+ if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, persona)) {
+ goto done;
+ }
+
+ d = opendir(path);
+ if (d == NULL) {
+ goto done;
+ }
+ dfd = dirfd(d);
+
+ /* most stuff in the pkgdir is data, except for the "cache"
+ * directory and below, which is cache, and the "lib" directory
+ * and below, which is code...
+ */
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+
+ if (de->d_type == DT_DIR) {
+ int subfd;
+ int64_t statsize = 0;
+ int64_t dirsize = 0;
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ statsize = stat_size(&s);
+ }
+ subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+ if (subfd >= 0) {
+ dirsize = calculate_dir_size(subfd);
+ }
+ if(!strcmp(name,"lib")) {
+ codesize += dirsize + statsize;
+ } else if(!strcmp(name,"cache")) {
+ cachesize += dirsize + statsize;
+ } else {
+ datasize += dirsize + statsize;
+ }
+ } else if (de->d_type == DT_LNK && !strcmp(name,"lib")) {
+ // This is the symbolic link to the application's library
+ // code. We'll count this as code instead of data, since
+ // it is not something that the app creates.
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ codesize += stat_size(&s);
+ }
+ } else {
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ datasize += stat_size(&s);
+ }
+ }
+ }
+ closedir(d);
+done:
+ *_codesize = codesize;
+ *_datasize = datasize;
+ *_cachesize = cachesize;
+ *_asecsize = asecsize;
+ return 0;
+}
+
+
+/* a simpler version of dexOptGenerateCacheFileName() */
+int create_cache_path(char path[PKG_PATH_MAX], const char *src)
+{
+ char *tmp;
+ int srclen;
+ int dstlen;
+
+ srclen = strlen(src);
+
+ /* demand that we are an absolute path */
+ if ((src == 0) || (src[0] != '/') || strstr(src,"..")) {
+ return -1;
+ }
+
+ if (srclen > PKG_PATH_MAX) { // XXX: PKG_NAME_MAX?
+ return -1;
+ }
+
+ dstlen = srclen + strlen(DALVIK_CACHE_PREFIX) +
+ strlen(DALVIK_CACHE_POSTFIX) + 1;
+
+ if (dstlen > PKG_PATH_MAX) {
+ return -1;
+ }
+
+ sprintf(path,"%s%s%s",
+ DALVIK_CACHE_PREFIX,
+ src + 1, /* skip the leading / */
+ DALVIK_CACHE_POSTFIX);
+
+ for(tmp = path + strlen(DALVIK_CACHE_PREFIX); *tmp; tmp++) {
+ if (*tmp == '/') {
+ *tmp = '@';
+ }
+ }
+
+ return 0;
+}
+
+static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name,
+ const char* dexopt_flags)
+{
+ static const char* DEX_OPT_BIN = "/system/bin/dexopt";
+ static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig
+ char zip_num[MAX_INT_LEN];
+ char odex_num[MAX_INT_LEN];
+
+ sprintf(zip_num, "%d", zip_fd);
+ sprintf(odex_num, "%d", odex_fd);
+
+ execl(DEX_OPT_BIN, DEX_OPT_BIN, "--zip", zip_num, odex_num, input_file_name,
+ dexopt_flags, (char*) NULL);
+ ALOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno));
+}
+
+static int wait_dexopt(pid_t pid, const char* apk_path)
+{
+ int status;
+ pid_t got_pid;
+
+ /*
+ * Wait for the optimization process to finish.
+ */
+ while (1) {
+ got_pid = waitpid(pid, &status, 0);
+ if (got_pid == -1 && errno == EINTR) {
+ printf("waitpid interrupted, retrying\n");
+ } else {
+ break;
+ }
+ }
+ if (got_pid != pid) {
+ ALOGW("waitpid failed: wanted %d, got %d: %s\n",
+ (int) pid, (int) got_pid, strerror(errno));
+ return 1;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ ALOGV("DexInv: --- END '%s' (success) ---\n", apk_path);
+ return 0;
+ } else {
+ ALOGW("DexInv: --- END '%s' --- status=0x%04x, process failed\n",
+ apk_path, status);
+ return status; /* always nonzero */
+ }
+}
+
+int dexopt(const char *apk_path, uid_t uid, int is_public)
+{
+ struct utimbuf ut;
+ struct stat apk_stat, dex_stat;
+ char dex_path[PKG_PATH_MAX];
+ char dexopt_flags[PROPERTY_VALUE_MAX];
+ char *end;
+ int res, zip_fd=-1, odex_fd=-1;
+
+ /* Before anything else: is there a .odex file? If so, we have
+ * pre-optimized the apk and there is nothing to do here.
+ */
+ if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) {
+ return -1;
+ }
+
+ /* platform-specific flags affecting optimization and verification */
+ property_get("dalvik.vm.dexopt-flags", dexopt_flags, "");
+
+ strcpy(dex_path, apk_path);
+ end = strrchr(dex_path, '.');
+ if (end != NULL) {
+ strcpy(end, ".odex");
+ if (stat(dex_path, &dex_stat) == 0) {
+ return 0;
+ }
+ }
+
+ if (create_cache_path(dex_path, apk_path)) {
+ return -1;
+ }
+
+ memset(&apk_stat, 0, sizeof(apk_stat));
+ stat(apk_path, &apk_stat);
+
+ zip_fd = open(apk_path, O_RDONLY, 0);
+ if (zip_fd < 0) {
+ ALOGE("dexopt cannot open '%s' for input\n", apk_path);
+ return -1;
+ }
+
+ unlink(dex_path);
+ odex_fd = open(dex_path, O_RDWR | O_CREAT | O_EXCL, 0644);
+ if (odex_fd < 0) {
+ ALOGE("dexopt cannot open '%s' for output\n", dex_path);
+ goto fail;
+ }
+ if (fchmod(odex_fd,
+ S_IRUSR|S_IWUSR|S_IRGRP |
+ (is_public ? S_IROTH : 0)) < 0) {
+ ALOGE("dexopt cannot chmod '%s'\n", dex_path);
+ goto fail;
+ }
+ if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {
+ ALOGE("dexopt cannot chown '%s'\n", dex_path);
+ goto fail;
+ }
+
+ ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
+
+ pid_t pid;
+ pid = fork();
+ if (pid == 0) {
+ /* child -- drop privileges before continuing */
+ if (setgid(uid) != 0) {
+ ALOGE("setgid(%d) failed during dexopt\n", uid);
+ exit(64);
+ }
+ if (setuid(uid) != 0) {
+ ALOGE("setuid(%d) during dexopt\n", uid);
+ exit(65);
+ }
+ // drop capabilities
+ 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;
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(66);
+ }
+ if (flock(odex_fd, LOCK_EX | LOCK_NB) != 0) {
+ ALOGE("flock(%s) failed: %s\n", dex_path, strerror(errno));
+ exit(67);
+ }
+
+ run_dexopt(zip_fd, odex_fd, apk_path, dexopt_flags);
+ exit(68); /* only get here on exec failure */
+ } else {
+ res = wait_dexopt(pid, apk_path);
+ if (res != 0) {
+ ALOGE("dexopt failed on '%s' res = %d\n", dex_path, res);
+ goto fail;
+ }
+ }
+
+ ut.actime = apk_stat.st_atime;
+ ut.modtime = apk_stat.st_mtime;
+ utime(dex_path, &ut);
+
+ close(odex_fd);
+ close(zip_fd);
+ return 0;
+
+fail:
+ if (odex_fd >= 0) {
+ close(odex_fd);
+ unlink(dex_path);
+ }
+ if (zip_fd >= 0) {
+ close(zip_fd);
+ }
+ return -1;
+}
+
+void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
+ struct stat* statbuf)
+{
+ while (path[basepos] != 0) {
+ if (path[basepos] == '/') {
+ path[basepos] = 0;
+ if (lstat(path, statbuf) < 0) {
+ ALOGV("Making directory: %s\n", path);
+ if (mkdir(path, mode) == 0) {
+ chown(path, uid, gid);
+ } else {
+ ALOGW("Unable to make directory %s: %s\n", path, strerror(errno));
+ }
+ }
+ path[basepos] = '/';
+ basepos++;
+ }
+ basepos++;
+ }
+}
+
+int movefileordir(char* srcpath, char* dstpath, int dstbasepos,
+ int dstuid, int dstgid, struct stat* statbuf)
+{
+ DIR *d;
+ struct dirent *de;
+ int res;
+
+ int srcend = strlen(srcpath);
+ int dstend = strlen(dstpath);
+
+ if (lstat(srcpath, statbuf) < 0) {
+ ALOGW("Unable to stat %s: %s\n", srcpath, strerror(errno));
+ return 1;
+ }
+
+ if ((statbuf->st_mode&S_IFDIR) == 0) {
+ mkinnerdirs(dstpath, dstbasepos, S_IRWXU|S_IRWXG|S_IXOTH,
+ dstuid, dstgid, statbuf);
+ ALOGV("Renaming %s to %s (uid %d)\n", srcpath, dstpath, dstuid);
+ if (rename(srcpath, dstpath) >= 0) {
+ if (chown(dstpath, dstuid, dstgid) < 0) {
+ ALOGE("cannot chown %s: %s\n", dstpath, strerror(errno));
+ unlink(dstpath);
+ return 1;
+ }
+ } else {
+ ALOGW("Unable to rename %s to %s: %s\n",
+ srcpath, dstpath, strerror(errno));
+ return 1;
+ }
+ return 0;
+ }
+
+ d = opendir(srcpath);
+ if (d == NULL) {
+ ALOGW("Unable to opendir %s: %s\n", srcpath, strerror(errno));
+ return 1;
+ }
+
+ res = 0;
+
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ if ((srcend+strlen(name)) >= (PKG_PATH_MAX-2)) {
+ ALOGW("Source path too long; skipping: %s/%s\n", srcpath, name);
+ continue;
+ }
+
+ if ((dstend+strlen(name)) >= (PKG_PATH_MAX-2)) {
+ ALOGW("Destination path too long; skipping: %s/%s\n", dstpath, name);
+ continue;
+ }
+
+ srcpath[srcend] = dstpath[dstend] = '/';
+ strcpy(srcpath+srcend+1, name);
+ strcpy(dstpath+dstend+1, name);
+
+ if (movefileordir(srcpath, dstpath, dstbasepos, dstuid, dstgid, statbuf) != 0) {
+ res = 1;
+ }
+
+ // Note: we will be leaving empty directories behind in srcpath,
+ // but that is okay, the package manager will be erasing all of the
+ // data associated with .apks that disappear.
+
+ srcpath[srcend] = dstpath[dstend] = 0;
+ }
+
+ closedir(d);
+ return res;
+}
+
+int movefiles()
+{
+ DIR *d;
+ int dfd, subfd;
+ struct dirent *de;
+ struct stat s;
+ char buf[PKG_PATH_MAX+1];
+ int bufp, bufe, bufi, readlen;
+
+ char srcpkg[PKG_NAME_MAX];
+ char dstpkg[PKG_NAME_MAX];
+ char srcpath[PKG_PATH_MAX];
+ char dstpath[PKG_PATH_MAX];
+ int dstuid=-1, dstgid=-1;
+ int hasspace;
+
+ d = opendir(UPDATE_COMMANDS_DIR_PREFIX);
+ if (d == NULL) {
+ goto done;
+ }
+ dfd = dirfd(d);
+
+ /* Iterate through all files in the directory, executing the
+ * file movements requested there-in.
+ */
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+
+ if (de->d_type == DT_DIR) {
+ continue;
+ } else {
+ subfd = openat(dfd, name, O_RDONLY);
+ if (subfd < 0) {
+ ALOGW("Unable to open update commands at %s%s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name);
+ continue;
+ }
+
+ bufp = 0;
+ bufe = 0;
+ buf[PKG_PATH_MAX] = 0;
+ srcpkg[0] = dstpkg[0] = 0;
+ while (1) {
+ bufi = bufp;
+ while (bufi < bufe && buf[bufi] != '\n') {
+ bufi++;
+ }
+ if (bufi < bufe) {
+ buf[bufi] = 0;
+ ALOGV("Processing line: %s\n", buf+bufp);
+ hasspace = 0;
+ while (bufp < bufi && isspace(buf[bufp])) {
+ hasspace = 1;
+ bufp++;
+ }
+ if (buf[bufp] == '#' || bufp == bufi) {
+ // skip comments and empty lines.
+ } else if (hasspace) {
+ if (dstpkg[0] == 0) {
+ ALOGW("Path before package line in %s%s: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+ } else if (srcpkg[0] == 0) {
+ // Skip -- source package no longer exists.
+ } else {
+ ALOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg);
+ if (!create_move_path(srcpath, srcpkg, buf+bufp, 0) &&
+ !create_move_path(dstpath, dstpkg, buf+bufp, 0)) {
+ movefileordir(srcpath, dstpath,
+ strlen(dstpath)-strlen(buf+bufp),
+ dstuid, dstgid, &s);
+ }
+ }
+ } else {
+ char* div = strchr(buf+bufp, ':');
+ if (div == NULL) {
+ ALOGW("Bad package spec in %s%s; no ':' sep: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+ } else {
+ *div = 0;
+ div++;
+ if (strlen(buf+bufp) < PKG_NAME_MAX) {
+ strcpy(dstpkg, buf+bufp);
+ } else {
+ srcpkg[0] = dstpkg[0] = 0;
+ ALOGW("Package name too long in %s%s: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+ }
+ if (strlen(div) < PKG_NAME_MAX) {
+ strcpy(srcpkg, div);
+ } else {
+ srcpkg[0] = dstpkg[0] = 0;
+ ALOGW("Package name too long in %s%s: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, div);
+ }
+ if (srcpkg[0] != 0) {
+ if (!create_pkg_path(srcpath, srcpkg, PKG_DIR_POSTFIX, 0)) {
+ if (lstat(srcpath, &s) < 0) {
+ // Package no longer exists -- skip.
+ srcpkg[0] = 0;
+ }
+ } else {
+ srcpkg[0] = 0;
+ ALOGW("Can't create path %s in %s%s\n",
+ div, UPDATE_COMMANDS_DIR_PREFIX, name);
+ }
+ if (srcpkg[0] != 0) {
+ if (!create_pkg_path(dstpath, dstpkg, PKG_DIR_POSTFIX, 0)) {
+ if (lstat(dstpath, &s) == 0) {
+ dstuid = s.st_uid;
+ dstgid = s.st_gid;
+ } else {
+ // Destination package doesn't
+ // exist... due to original-package,
+ // this is normal, so don't be
+ // noisy about it.
+ srcpkg[0] = 0;
+ }
+ } else {
+ srcpkg[0] = 0;
+ ALOGW("Can't create path %s in %s%s\n",
+ div, UPDATE_COMMANDS_DIR_PREFIX, name);
+ }
+ }
+ ALOGV("Transfering from %s to %s: uid=%d\n",
+ srcpkg, dstpkg, dstuid);
+ }
+ }
+ }
+ bufp = bufi+1;
+ } else {
+ if (bufp == 0) {
+ if (bufp < bufe) {
+ ALOGW("Line too long in %s%s, skipping: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, buf);
+ }
+ } else if (bufp < bufe) {
+ memcpy(buf, buf+bufp, bufe-bufp);
+ bufe -= bufp;
+ bufp = 0;
+ }
+ readlen = read(subfd, buf+bufe, PKG_PATH_MAX-bufe);
+ if (readlen < 0) {
+ ALOGW("Failure reading update commands in %s%s: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, strerror(errno));
+ break;
+ } else if (readlen == 0) {
+ break;
+ }
+ bufe += readlen;
+ buf[bufe] = 0;
+ ALOGV("Read buf: %s\n", buf);
+ }
+ }
+ close(subfd);
+ }
+ }
+ closedir(d);
+done:
+ return 0;
+}
+
+int linklib(const char* pkgname, const char* asecLibDir, int userId)
+{
+ char pkgdir[PKG_PATH_MAX];
+ char libsymlink[PKG_PATH_MAX];
+ struct stat s, libStat;
+ int rc = 0;
+
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userId)) {
+ ALOGE("cannot create package path\n");
+ return -1;
+ }
+ if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userId)) {
+ ALOGE("cannot create package lib symlink origin path\n");
+ return -1;
+ }
+
+ if (stat(pkgdir, &s) < 0) return -1;
+
+ if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {
+ ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno));
+ return -1;
+ }
+
+ if (chmod(pkgdir, 0700) < 0) {
+ ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
+ rc = -1;
+ goto out;
+ }
+
+ if (lstat(libsymlink, &libStat) < 0) {
+ if (errno != ENOENT) {
+ ALOGE("couldn't stat lib dir: %s\n", strerror(errno));
+ rc = -1;
+ goto out;
+ }
+ } else {
+ if (S_ISDIR(libStat.st_mode)) {
+ if (delete_dir_contents(libsymlink, 1, 0) < 0) {
+ rc = -1;
+ goto out;
+ }
+ } else if (S_ISLNK(libStat.st_mode)) {
+ if (unlink(libsymlink) < 0) {
+ ALOGE("couldn't unlink lib dir: %s\n", strerror(errno));
+ rc = -1;
+ goto out;
+ }
+ }
+ }
+
+ if (symlink(asecLibDir, libsymlink) < 0) {
+ ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir,
+ strerror(errno));
+ rc = -errno;
+ goto out;
+ }
+
+out:
+ if (chmod(pkgdir, s.st_mode) < 0) {
+ ALOGE("linklib() 2: failed to chmod '%s': %s\n", pkgdir, strerror(errno));
+ rc = -errno;
+ }
+
+ if (chown(pkgdir, s.st_uid, s.st_gid) < 0) {
+ ALOGE("failed to chown '%s' : %s\n", pkgdir, strerror(errno));
+ return -errno;
+ }
+
+ return rc;
+}
--- /dev/null
+/*
+** Copyright 2008, 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 <linux/capability.h>
+#include <linux/prctl.h>
+
+#include "installd.h"
+
+
+#define BUFFER_MAX 1024 /* input buffer for commands */
+#define TOKEN_MAX 8 /* max number of arguments in buffer */
+#define REPLY_MAX 256 /* largest reply allowed */
+
+static int do_ping(char **arg, char reply[REPLY_MAX])
+{
+ return 0;
+}
+
+static int do_install(char **arg, char reply[REPLY_MAX])
+{
+ return install(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, gid */
+}
+
+static int do_dexopt(char **arg, char reply[REPLY_MAX])
+{
+ /* apk_path, uid, is_public */
+ return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]));
+}
+
+static int do_move_dex(char **arg, char reply[REPLY_MAX])
+{
+ return move_dex(arg[0], arg[1]); /* src, dst */
+}
+
+static int do_rm_dex(char **arg, char reply[REPLY_MAX])
+{
+ return rm_dex(arg[0]); /* pkgname */
+}
+
+static int do_remove(char **arg, char reply[REPLY_MAX])
+{
+ return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */
+}
+
+static int do_rename(char **arg, char reply[REPLY_MAX])
+{
+ return renamepkg(arg[0], arg[1]); /* oldpkgname, newpkgname */
+}
+
+static int do_fixuid(char **arg, char reply[REPLY_MAX])
+{
+ return fix_uid(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, gid */
+}
+
+static int do_free_cache(char **arg, char reply[REPLY_MAX]) /* TODO int:free_size */
+{
+ return free_cache((int64_t)atoll(arg[0])); /* free_size */
+}
+
+static int do_rm_cache(char **arg, char reply[REPLY_MAX])
+{
+ return delete_cache(arg[0], atoi(arg[1])); /* pkgname, userid */
+}
+
+static int do_get_size(char **arg, char reply[REPLY_MAX])
+{
+ int64_t codesize = 0;
+ int64_t datasize = 0;
+ int64_t cachesize = 0;
+ int64_t asecsize = 0;
+ int res = 0;
+
+ /* pkgdir, persona, apkpath */
+ res = get_size(arg[0], atoi(arg[1]), arg[2], arg[3], arg[4],
+ &codesize, &datasize, &cachesize, &asecsize);
+
+ /*
+ * Each int64_t can take up 22 characters printed out. Make sure it
+ * doesn't go over REPLY_MAX in the future.
+ */
+ snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64,
+ codesize, datasize, cachesize, asecsize);
+ return res;
+}
+
+static int do_rm_user_data(char **arg, char reply[REPLY_MAX])
+{
+ return delete_user_data(arg[0], atoi(arg[1])); /* pkgname, userid */
+}
+
+static int do_mk_user_data(char **arg, char reply[REPLY_MAX])
+{
+ return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, userid */
+}
+
+static int do_rm_user(char **arg, char reply[REPLY_MAX])
+{
+ return delete_persona(atoi(arg[0])); /* userid */
+}
+
+static int do_movefiles(char **arg, char reply[REPLY_MAX])
+{
+ return movefiles();
+}
+
+static int do_linklib(char **arg, char reply[REPLY_MAX])
+{
+ return linklib(arg[0], arg[1], atoi(arg[2]));
+}
+
+struct cmdinfo {
+ const char *name;
+ unsigned numargs;
+ int (*func)(char **arg, char reply[REPLY_MAX]);
+};
+
+struct cmdinfo cmds[] = {
+ { "ping", 0, do_ping },
+ { "install", 3, do_install },
+ { "dexopt", 3, do_dexopt },
+ { "movedex", 2, do_move_dex },
+ { "rmdex", 1, do_rm_dex },
+ { "remove", 2, do_remove },
+ { "rename", 2, do_rename },
+ { "fixuid", 3, do_fixuid },
+ { "freecache", 1, do_free_cache },
+ { "rmcache", 2, do_rm_cache },
+ { "getsize", 5, do_get_size },
+ { "rmuserdata", 2, do_rm_user_data },
+ { "movefiles", 0, do_movefiles },
+ { "linklib", 3, do_linklib },
+ { "mkuserdata", 3, do_mk_user_data },
+ { "rmuser", 1, do_rm_user },
+};
+
+static int readx(int s, void *_buf, int count)
+{
+ char *buf = _buf;
+ int n = 0, r;
+ if (count < 0) return -1;
+ while (n < count) {
+ r = read(s, buf + n, count - n);
+ if (r < 0) {
+ if (errno == EINTR) continue;
+ ALOGE("read error: %s\n", strerror(errno));
+ return -1;
+ }
+ if (r == 0) {
+ ALOGE("eof\n");
+ return -1; /* EOF */
+ }
+ n += r;
+ }
+ return 0;
+}
+
+static int writex(int s, const void *_buf, int count)
+{
+ const char *buf = _buf;
+ int n = 0, r;
+ if (count < 0) return -1;
+ while (n < count) {
+ r = write(s, buf + n, count - n);
+ if (r < 0) {
+ if (errno == EINTR) continue;
+ ALOGE("write error: %s\n", strerror(errno));
+ return -1;
+ }
+ n += r;
+ }
+ return 0;
+}
+
+
+/* Tokenize the command buffer, locate a matching command,
+ * ensure that the required number of arguments are provided,
+ * call the function(), return the result.
+ */
+static int execute(int s, char cmd[BUFFER_MAX])
+{
+ char reply[REPLY_MAX];
+ char *arg[TOKEN_MAX+1];
+ unsigned i;
+ unsigned n = 0;
+ unsigned short count;
+ int ret = -1;
+
+// ALOGI("execute('%s')\n", cmd);
+
+ /* default reply is "" */
+ reply[0] = 0;
+
+ /* n is number of args (not counting arg[0]) */
+ arg[0] = cmd;
+ while (*cmd) {
+ if (isspace(*cmd)) {
+ *cmd++ = 0;
+ n++;
+ arg[n] = cmd;
+ if (n == TOKEN_MAX) {
+ ALOGE("too many arguments\n");
+ goto done;
+ }
+ }
+ cmd++;
+ }
+
+ for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
+ if (!strcmp(cmds[i].name,arg[0])) {
+ if (n != cmds[i].numargs) {
+ ALOGE("%s requires %d arguments (%d given)\n",
+ cmds[i].name, cmds[i].numargs, n);
+ } else {
+ ret = cmds[i].func(arg + 1, reply);
+ }
+ goto done;
+ }
+ }
+ ALOGE("unsupported command '%s'\n", arg[0]);
+
+done:
+ if (reply[0]) {
+ n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
+ } else {
+ n = snprintf(cmd, BUFFER_MAX, "%d", ret);
+ }
+ if (n > BUFFER_MAX) n = BUFFER_MAX;
+ count = n;
+
+// ALOGI("reply: '%s'\n", cmd);
+ if (writex(s, &count, sizeof(count))) return -1;
+ if (writex(s, cmd, count)) return -1;
+ return 0;
+}
+
+/**
+ * Initialize all the global variables that are used elsewhere. Returns 0 upon
+ * success and -1 on error.
+ */
+void free_globals() {
+ size_t i;
+
+ for (i = 0; i < android_system_dirs.count; i++) {
+ if (android_system_dirs.dirs[i].path != NULL) {
+ free(android_system_dirs.dirs[i].path);
+ }
+ }
+
+ free(android_system_dirs.dirs);
+}
+
+int initialize_globals() {
+ // Get the android data directory.
+ if (get_path_from_env(&android_data_dir, "ANDROID_DATA") < 0) {
+ return -1;
+ }
+
+ // Get the android app directory.
+ if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) {
+ return -1;
+ }
+
+ // Get the android protected app directory.
+ if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) {
+ return -1;
+ }
+
+ // Get the android app native library directory.
+ if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0) {
+ return -1;
+ }
+
+ // Get the sd-card ASEC mount point.
+ if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) {
+ return -1;
+ }
+
+ // Get the android media directory.
+ if (copy_and_append(&android_media_dir, &android_data_dir, MEDIA_SUBDIR) < 0) {
+ return -1;
+ }
+
+ // Take note of the system and vendor directories.
+ android_system_dirs.count = 2;
+
+ android_system_dirs.dirs = calloc(android_system_dirs.count, sizeof(dir_rec_t));
+ if (android_system_dirs.dirs == NULL) {
+ ALOGE("Couldn't allocate array for dirs; aborting\n");
+ return -1;
+ }
+
+ // system
+ if (get_path_from_env(&android_system_dirs.dirs[0], "ANDROID_ROOT") < 0) {
+ free_globals();
+ return -1;
+ }
+
+ // append "app/" to dirs[0]
+ char *system_app_path = build_string2(android_system_dirs.dirs[0].path, APP_SUBDIR);
+ android_system_dirs.dirs[0].path = system_app_path;
+ android_system_dirs.dirs[0].len = strlen(system_app_path);
+
+ // vendor
+ // TODO replace this with an environment variable (doesn't exist yet)
+ android_system_dirs.dirs[1].path = "/vendor/app/";
+ android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path);
+
+ return 0;
+}
+
+int initialize_directories() {
+ int res = -1;
+
+ // Read current filesystem layout version to handle upgrade paths
+ char version_path[PATH_MAX];
+ snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path);
+
+ int oldVersion;
+ if (fs_read_atomic_int(version_path, &oldVersion) == -1) {
+ oldVersion = 0;
+ }
+ int version = oldVersion;
+
+ // /data/user
+ char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX);
+ // /data/data
+ char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX);
+ // /data/user/0
+ char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, "0");
+ if (!user_data_dir || !legacy_data_dir || !primary_data_dir) {
+ goto fail;
+ }
+
+ // Make the /data/user directory if necessary
+ if (access(user_data_dir, R_OK) < 0) {
+ if (mkdir(user_data_dir, 0711) < 0) {
+ goto fail;
+ }
+ if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {
+ goto fail;
+ }
+ if (chmod(user_data_dir, 0711) < 0) {
+ goto fail;
+ }
+ }
+ // Make the /data/user/0 symlink to /data/data if necessary
+ if (access(primary_data_dir, R_OK) < 0) {
+ if (symlink(legacy_data_dir, primary_data_dir)) {
+ goto fail;
+ }
+ }
+
+ if (version == 0) {
+ // Introducing multi-user, so migrate /data/media contents into /data/media/0
+ ALOGD("Upgrading /data/media for multi-user");
+
+ // Ensure /data/media
+ if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ goto fail;
+ }
+
+ // /data/media.tmp
+ char media_tmp_dir[PATH_MAX];
+ snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path);
+
+ // Only copy when upgrade not already in progress
+ if (access(media_tmp_dir, F_OK) == -1) {
+ if (rename(android_media_dir.path, media_tmp_dir) == -1) {
+ ALOGE("Failed to move legacy media path: %s", strerror(errno));
+ goto fail;
+ }
+ }
+
+ // Create /data/media again
+ if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ goto fail;
+ }
+
+ // /data/media/0
+ char owner_media_dir[PATH_MAX];
+ snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path);
+
+ // Move any owner data into place
+ if (access(media_tmp_dir, F_OK) == 0) {
+ if (rename(media_tmp_dir, owner_media_dir) == -1) {
+ ALOGE("Failed to move owner media path: %s", strerror(errno));
+ goto fail;
+ }
+ }
+
+ // Ensure media directories for any existing users
+ DIR *dir;
+ struct dirent *dirent;
+ char user_media_dir[PATH_MAX];
+
+ dir = opendir(user_data_dir);
+ if (dir != NULL) {
+ while ((dirent = readdir(dir))) {
+ if (dirent->d_type == DT_DIR) {
+ const char *name = dirent->d_name;
+
+ // skip "." and ".."
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ // /data/media/<user_id>
+ snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name);
+ if (fs_prepare_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ goto fail;
+ }
+ }
+ }
+ closedir(dir);
+ }
+
+ version = 1;
+ }
+
+ // /data/media/obb
+ char media_obb_dir[PATH_MAX];
+ snprintf(media_obb_dir, PATH_MAX, "%sobb", android_media_dir.path);
+
+ if (version == 1) {
+ // Introducing /data/media/obb for sharing OBB across users; migrate
+ // any existing OBB files from owner.
+ ALOGD("Upgrading to shared /data/media/obb");
+
+ // /data/media/0/Android/obb
+ char owner_obb_path[PATH_MAX];
+ snprintf(owner_obb_path, PATH_MAX, "%s0/Android/obb", android_media_dir.path);
+
+ // Only move if target doesn't already exist
+ if (access(media_obb_dir, F_OK) != 0 && access(owner_obb_path, F_OK) == 0) {
+ if (rename(owner_obb_path, media_obb_dir) == -1) {
+ ALOGE("Failed to move OBB from owner: %s", strerror(errno));
+ goto fail;
+ }
+ }
+
+ version = 2;
+ }
+
+ if (ensure_media_user_dirs(0) == -1) {
+ ALOGE("Failed to setup media for user 0");
+ goto fail;
+ }
+ if (fs_prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ goto fail;
+ }
+
+ // Persist layout version if changed
+ if (version != oldVersion) {
+ if (fs_write_atomic_int(version_path, version) == -1) {
+ ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
+ goto fail;
+ }
+ }
+
+ // Success!
+ res = 0;
+
+fail:
+ free(user_data_dir);
+ free(legacy_data_dir);
+ free(primary_data_dir);
+ return res;
+}
+
+static void drop_privileges() {
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (setgid(AID_INSTALL) < 0) {
+ ALOGE("setgid() can't drop privileges; exiting.\n");
+ exit(1);
+ }
+
+ if (setuid(AID_INSTALL) < 0) {
+ ALOGE("setuid() can't drop privileges; exiting.\n");
+ exit(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_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
+ capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
+ capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
+ capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+
+ capdata[0].effective = capdata[0].permitted;
+ capdata[1].effective = capdata[1].permitted;
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
+int main(const int argc, const char *argv[]) {
+ char buf[BUFFER_MAX];
+ struct sockaddr addr;
+ socklen_t alen;
+ int lsocket, s, count;
+
+ ALOGI("installd firing up\n");
+
+ if (initialize_globals() < 0) {
+ ALOGE("Could not initialize globals; exiting.\n");
+ exit(1);
+ }
+
+ if (initialize_directories() < 0) {
+ ALOGE("Could not create directories; exiting.\n");
+ exit(1);
+ }
+
+ drop_privileges();
+
+ lsocket = android_get_control_socket(SOCKET_PATH);
+ if (lsocket < 0) {
+ ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (listen(lsocket, 5)) {
+ ALOGE("Listen on socket failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ fcntl(lsocket, F_SETFD, FD_CLOEXEC);
+
+ for (;;) {
+ alen = sizeof(addr);
+ s = accept(lsocket, &addr, &alen);
+ if (s < 0) {
+ ALOGE("Accept failed: %s\n", strerror(errno));
+ continue;
+ }
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+
+ ALOGI("new connection\n");
+ for (;;) {
+ unsigned short count;
+ if (readx(s, &count, sizeof(count))) {
+ ALOGE("failed to read size\n");
+ break;
+ }
+ if ((count < 1) || (count >= BUFFER_MAX)) {
+ ALOGE("invalid size %d\n", count);
+ break;
+ }
+ if (readx(s, buf, count)) {
+ ALOGE("failed to read command\n");
+ break;
+ }
+ buf[count] = 0;
+ if (execute(s, buf)) break;
+ }
+ ALOGI("closing connection\n");
+ close(s);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+**
+** Copyright 2008, 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.
+*/
+
+#define LOG_TAG "installd"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <cutils/fs.h>
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <cutils/multiuser.h>
+
+#include <private/android_filesystem_config.h>
+
+#if INCLUDE_SYS_MOUNT_FOR_STATFS
+#include <sys/mount.h>
+#else
+#include <sys/statfs.h>
+#endif
+
+#define SOCKET_PATH "installd"
+
+
+/* elements combined with a valid package name to form paths */
+
+#define PRIMARY_USER_PREFIX "data/"
+#define SECONDARY_USER_PREFIX "user/"
+
+#define PKG_DIR_POSTFIX ""
+
+#define PKG_LIB_POSTFIX "/lib"
+
+#define CACHE_DIR_POSTFIX "/cache"
+
+#define APP_SUBDIR "app/" // sub-directory under ANDROID_DATA
+
+#define APP_LIB_SUBDIR "app-lib/" // sub-directory under ANDROID_DATA
+
+#define MEDIA_SUBDIR "media/" // sub-directory under ANDROID_DATA
+
+/* other handy constants */
+
+#define PRIVATE_APP_SUBDIR "app-private/" // sub-directory under ANDROID_DATA
+
+#define DALVIK_CACHE_PREFIX "/data/dalvik-cache/"
+#define DALVIK_CACHE_POSTFIX "/classes.dex"
+
+#define UPDATE_COMMANDS_DIR_PREFIX "/system/etc/updatecmds/"
+
+#define PKG_NAME_MAX 128 /* largest allowed package name */
+#define PKG_PATH_MAX 256 /* max size of any path we use */
+
+#define PER_USER_RANGE ((uid_t)100000) /* range of uids per user
+ uid = persona * PER_USER_RANGE + appid */
+
+/* data structures */
+
+typedef struct {
+ char* path;
+ size_t len;
+} dir_rec_t;
+
+typedef struct {
+ size_t count;
+ dir_rec_t* dirs;
+} dir_rec_array_t;
+
+extern dir_rec_t android_app_dir;
+extern dir_rec_t android_app_private_dir;
+extern dir_rec_t android_app_lib_dir;
+extern dir_rec_t android_data_dir;
+extern dir_rec_t android_asec_dir;
+extern dir_rec_t android_media_dir;
+extern dir_rec_array_t android_system_dirs;
+
+typedef struct cache_dir_struct {
+ struct cache_dir_struct* parent;
+ int32_t childCount;
+ int32_t hiddenCount;
+ int32_t deleted;
+ char name[];
+} cache_dir_t;
+
+typedef struct {
+ cache_dir_t* dir;
+ time_t modTime;
+ char name[];
+} cache_file_t;
+
+typedef struct {
+ size_t numDirs;
+ size_t availDirs;
+ cache_dir_t** dirs;
+ size_t numFiles;
+ size_t availFiles;
+ cache_file_t** files;
+ size_t numCollected;
+ void* memBlocks;
+ int8_t* curMemBlockAvail;
+ int8_t* curMemBlockEnd;
+} cache_t;
+
+/* util.c */
+
+int create_pkg_path_in_dir(char path[PKG_PATH_MAX],
+ const dir_rec_t* dir,
+ const char* pkgname,
+ const char* postfix);
+
+int create_pkg_path(char path[PKG_PATH_MAX],
+ const char *pkgname,
+ const char *postfix,
+ uid_t persona);
+
+int create_persona_path(char path[PKG_PATH_MAX],
+ uid_t persona);
+
+int create_persona_media_path(char path[PKG_PATH_MAX], userid_t userid);
+
+int create_move_path(char path[PKG_PATH_MAX],
+ const char* pkgname,
+ const char* leaf,
+ uid_t persona);
+
+int is_valid_package_name(const char* pkgname);
+
+int create_cache_path(char path[PKG_PATH_MAX], const char *src);
+
+int delete_dir_contents(const char *pathname,
+ int also_delete_dir,
+ const char *ignore);
+
+int delete_dir_contents_fd(int dfd, const char *name);
+
+int lookup_media_dir(char basepath[PATH_MAX], const char *dir);
+
+int64_t data_disk_free();
+
+cache_t* start_cache_collection();
+
+void add_cache_files(cache_t* cache, const char *basepath, const char *cachedir);
+
+void clear_cache_files(cache_t* cache, int64_t free_size);
+
+void finish_cache_collection(cache_t* cache);
+
+int validate_system_app_path(const char* path);
+
+int get_path_from_env(dir_rec_t* rec, const char* var);
+
+int get_path_from_string(dir_rec_t* rec, const char* path);
+
+int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix);
+
+int validate_apk_path(const char *path);
+
+int append_and_increment(char** dst, const char* src, size_t* dst_size);
+
+char *build_string2(char *s1, char *s2);
+char *build_string3(char *s1, char *s2, char *s3);
+
+int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
+int ensure_media_user_dirs(userid_t userid);
+
+/* commands.c */
+
+int install(const char *pkgname, uid_t uid, gid_t gid);
+int uninstall(const char *pkgname, uid_t persona);
+int renamepkg(const char *oldpkgname, const char *newpkgname);
+int fix_uid(const char *pkgname, uid_t uid, gid_t gid);
+int delete_user_data(const char *pkgname, uid_t persona);
+int make_user_data(const char *pkgname, uid_t uid, uid_t persona);
+int delete_persona(uid_t persona);
+int delete_cache(const char *pkgname, uid_t persona);
+int move_dex(const char *src, const char *dst);
+int rm_dex(const char *path);
+int protect(char *pkgname, gid_t gid);
+int get_size(const char *pkgname, int persona, const char *apkpath, const char *fwdlock_apkpath,
+ const char *asecpath, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
+ int64_t *asecsize);
+int free_cache(int64_t free_size);
+int dexopt(const char *apk_path, uid_t uid, int is_public);
+int movefiles();
+int linklib(const char* target, const char* source, int userId);
--- /dev/null
+# Build the unit tests for installd
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Build the unit tests.
+test_src_files := \
+ installd_utils_test.cpp
+
+shared_libraries := \
+ libutils \
+ libcutils \
+ libstlport
+
+static_libraries := \
+ libinstalld \
+ libdiskusage \
+ libgtest \
+ libgtest_main
+
+c_includes := \
+ frameworks/base/cmds/installd
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval include $(BUILD_NATIVE_TEST)) \
+)
--- /dev/null
+/*
+ * Copyright (C) 2011 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 <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "utils_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "installd.h"
+}
+
+#define TEST_DATA_DIR "/data/"
+#define TEST_APP_DIR "/data/app/"
+#define TEST_APP_PRIVATE_DIR "/data/app-private/"
+#define TEST_ASEC_DIR "/mnt/asec/"
+
+#define TEST_SYSTEM_DIR1 "/system/app/"
+#define TEST_SYSTEM_DIR2 "/vendor/app/"
+
+#define REALLY_LONG_APP_NAME "com.example." \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+#define REALLY_LONG_LEAF_NAME "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
+ "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
+ "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_" \
+ "shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_shared_prefs_"
+
+namespace android {
+
+class UtilsTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ android_app_dir.path = TEST_APP_DIR;
+ android_app_dir.len = strlen(TEST_APP_DIR);
+
+ android_app_private_dir.path = TEST_APP_PRIVATE_DIR;
+ android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR);
+
+ android_data_dir.path = TEST_DATA_DIR;
+ android_data_dir.len = strlen(TEST_DATA_DIR);
+
+ android_asec_dir.path = TEST_ASEC_DIR;
+ android_asec_dir.len = strlen(TEST_ASEC_DIR);
+
+ android_system_dirs.count = 2;
+
+ android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t));
+ android_system_dirs.dirs[0].path = TEST_SYSTEM_DIR1;
+ android_system_dirs.dirs[0].len = strlen(TEST_SYSTEM_DIR1);
+
+ android_system_dirs.dirs[1].path = TEST_SYSTEM_DIR2;
+ android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2);
+ }
+
+ virtual void TearDown() {
+ free(android_system_dirs.dirs);
+ }
+};
+
+TEST_F(UtilsTest, IsValidApkPath_BadPrefix) {
+ // Bad prefixes directories
+ const char *badprefix1 = "/etc/passwd";
+ EXPECT_EQ(-1, validate_apk_path(badprefix1))
+ << badprefix1 << " should be allowed as a valid path";
+
+ const char *badprefix2 = "../.." TEST_APP_DIR "../../../blah";
+ EXPECT_EQ(-1, validate_apk_path(badprefix2))
+ << badprefix2 << " should be allowed as a valid path";
+
+ const char *badprefix3 = "init.rc";
+ EXPECT_EQ(-1, validate_apk_path(badprefix3))
+ << badprefix3 << " should be allowed as a valid path";
+
+ const char *badprefix4 = "/init.rc";
+ EXPECT_EQ(-1, validate_apk_path(badprefix4))
+ << badprefix4 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_Internal) {
+ // Internal directories
+ const char *internal1 = TEST_APP_DIR "example.apk";
+ EXPECT_EQ(0, validate_apk_path(internal1))
+ << internal1 << " should be allowed as a valid path";
+
+ const char *badint1 = TEST_APP_DIR "../example.apk";
+ EXPECT_EQ(-1, validate_apk_path(badint1))
+ << badint1 << " should be rejected as a invalid path";
+
+ const char *badint2 = TEST_APP_DIR "/../example.apk";
+ EXPECT_EQ(-1, validate_apk_path(badint2))
+ << badint2 << " should be rejected as a invalid path";
+
+ const char *badint3 = TEST_APP_DIR "example.com/pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badint3))
+ << badint3 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_Private) {
+ // Internal directories
+ const char *private1 = TEST_APP_PRIVATE_DIR "example.apk";
+ EXPECT_EQ(0, validate_apk_path(private1))
+ << private1 << " should be allowed as a valid path";
+
+ const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk";
+ EXPECT_EQ(-1, validate_apk_path(badpriv1))
+ << badpriv1 << " should be rejected as a invalid path";
+
+ const char *badpriv2 = TEST_APP_PRIVATE_DIR "/../example.apk";
+ EXPECT_EQ(-1, validate_apk_path(badpriv2))
+ << badpriv2 << " should be rejected as a invalid path";
+
+ const char *badpriv3 = TEST_APP_PRIVATE_DIR "example.com/pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badpriv3))
+ << badpriv3 << " should be rejected as a invalid path";
+}
+
+
+TEST_F(UtilsTest, IsValidApkPath_AsecGood1) {
+ const char *asec1 = TEST_ASEC_DIR "example.apk";
+ EXPECT_EQ(0, validate_apk_path(asec1))
+ << asec1 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_AsecGood2) {
+ const char *asec2 = TEST_ASEC_DIR "com.example.asec/pkg.apk";
+ EXPECT_EQ(0, validate_apk_path(asec2))
+ << asec2 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_EscapeFail) {
+ const char *badasec1 = TEST_ASEC_DIR "../example.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec1))
+ << badasec1 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_DoubleSlashFail) {
+ const char *badasec2 = TEST_ASEC_DIR "com.example.asec//pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec2))
+ << badasec2 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeFail) {
+ const char *badasec3 = TEST_ASEC_DIR "com.example.asec/../../../pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec3))
+ << badasec3 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_SlashEscapeFail) {
+ const char *badasec4 = TEST_ASEC_DIR "/../example.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec4))
+ << badasec4 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_CrazyDirFail) {
+ const char *badasec5 = TEST_ASEC_DIR ".//../..";
+ EXPECT_EQ(-1, validate_apk_path(badasec5))
+ << badasec5 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeSingleFail) {
+ const char *badasec6 = TEST_ASEC_DIR "com.example.asec/../pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec6))
+ << badasec6 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) {
+ const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec7))
+ << badasec7 << " should be rejected as a invalid path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_Dir1) {
+ const char *sysapp1 = TEST_SYSTEM_DIR1 "Voice.apk";
+ EXPECT_EQ(0, validate_system_app_path(sysapp1))
+ << sysapp1 << " should be allowed as a system path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_Dir2) {
+ const char *sysapp2 = TEST_SYSTEM_DIR2 "com.example.myapp.apk";
+ EXPECT_EQ(0, validate_system_app_path(sysapp2))
+ << sysapp2 << " should be allowed as a system path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_EscapeFail) {
+ const char *badapp1 = TEST_SYSTEM_DIR1 "../com.example.apk";
+ EXPECT_EQ(-1, validate_system_app_path(badapp1))
+ << badapp1 << " should be rejected not a system path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_DoubleEscapeFail) {
+ const char *badapp2 = TEST_SYSTEM_DIR2 "/../../com.example.apk";
+ EXPECT_EQ(-1, validate_system_app_path(badapp2))
+ << badapp2 << " should be rejected not a system path";
+}
+
+TEST_F(UtilsTest, CheckSystemApp_BadPathEscapeFail) {
+ const char *badapp3 = TEST_APP_DIR "/../../com.example.apk";
+ EXPECT_EQ(-1, validate_system_app_path(badapp3))
+ << badapp3 << " should be rejected not a system path";
+}
+
+TEST_F(UtilsTest, GetPathFromString_NullPathFail) {
+ dir_rec_t test1;
+ EXPECT_EQ(-1, get_path_from_string(&test1, (const char *) NULL))
+ << "Should not allow NULL as a path.";
+}
+
+TEST_F(UtilsTest, GetPathFromString_EmptyPathFail) {
+ dir_rec_t test1;
+ EXPECT_EQ(-1, get_path_from_string(&test1, ""))
+ << "Should not allow empty paths.";
+}
+
+TEST_F(UtilsTest, GetPathFromString_RelativePathFail) {
+ dir_rec_t test1;
+ EXPECT_EQ(-1, get_path_from_string(&test1, "mnt/asec"))
+ << "Should not allow relative paths.";
+}
+
+TEST_F(UtilsTest, GetPathFromString_NonCanonical) {
+ dir_rec_t test1;
+
+ EXPECT_EQ(0, get_path_from_string(&test1, "/mnt/asec"))
+ << "Should be able to canonicalize directory /mnt/asec";
+ EXPECT_STREQ("/mnt/asec/", test1.path)
+ << "/mnt/asec should be canonicalized to /mnt/asec/";
+ EXPECT_EQ(10, (ssize_t) test1.len)
+ << "path len should be equal to the length of /mnt/asec/ (10)";
+ free(test1.path);
+}
+
+TEST_F(UtilsTest, GetPathFromString_CanonicalPath) {
+ dir_rec_t test3;
+ EXPECT_EQ(0, get_path_from_string(&test3, "/data/app/"))
+ << "Should be able to canonicalize directory /data/app/";
+ EXPECT_STREQ("/data/app/", test3.path)
+ << "/data/app/ should be canonicalized to /data/app/";
+ EXPECT_EQ(10, (ssize_t) test3.len)
+ << "path len should be equal to the length of /data/app/ (10)";
+ free(test3.path);
+}
+
+TEST_F(UtilsTest, CreatePkgPath_LongPkgNameSuccess) {
+ char path[PKG_PATH_MAX];
+
+ // Create long packagename of "aaaaa..."
+ size_t pkgnameSize = PKG_NAME_MAX;
+ char pkgname[pkgnameSize + 1];
+ memset(pkgname, 'a', pkgnameSize);
+ pkgname[pkgnameSize] = '\0';
+
+ EXPECT_EQ(0, create_pkg_path(path, pkgname, "", 0))
+ << "Should successfully be able to create package name.";
+
+ const char *prefix = TEST_DATA_DIR PRIMARY_USER_PREFIX;
+ size_t offset = strlen(prefix);
+ EXPECT_STREQ(pkgname, path + offset)
+ << "Package path should be a really long string of a's";
+}
+
+TEST_F(UtilsTest, CreatePkgPath_LongPkgNameFail) {
+ char path[PKG_PATH_MAX];
+
+ // Create long packagename of "aaaaa..."
+ size_t pkgnameSize = PKG_NAME_MAX + 1;
+ char pkgname[pkgnameSize + 1];
+ memset(pkgname, 'a', pkgnameSize);
+ pkgname[pkgnameSize] = '\0';
+
+ EXPECT_EQ(-1, create_pkg_path(path, pkgname, "", 0))
+ << "Should return error because package name is too long.";
+}
+
+TEST_F(UtilsTest, CreatePkgPath_LongPostfixFail) {
+ char path[PKG_PATH_MAX];
+
+ // Create long packagename of "aaaaa..."
+ size_t postfixSize = PKG_PATH_MAX;
+ char postfix[postfixSize + 1];
+ memset(postfix, 'a', postfixSize);
+ postfix[postfixSize] = '\0';
+
+ EXPECT_EQ(-1, create_pkg_path(path, "com.example.package", postfix, 0))
+ << "Should return error because postfix is too long.";
+}
+
+TEST_F(UtilsTest, CreatePkgPath_PrimaryUser) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 0))
+ << "Should return error because postfix is too long.";
+
+ EXPECT_STREQ(TEST_DATA_DIR PRIMARY_USER_PREFIX "com.example.package", path)
+ << "Package path should be in /data/data/";
+}
+
+TEST_F(UtilsTest, CreatePkgPath_SecondaryUser) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(0, create_pkg_path(path, "com.example.package", "", 1))
+ << "Should successfully create package path.";
+
+ EXPECT_STREQ(TEST_DATA_DIR SECONDARY_USER_PREFIX "1/com.example.package", path)
+ << "Package path should be in /data/user/";
+}
+
+TEST_F(UtilsTest, CreatePkgPathInDir_ProtectedDir) {
+ char path[PKG_PATH_MAX];
+
+ dir_rec_t dir;
+ dir.path = "/data/app-private/";
+ dir.len = strlen(dir.path);
+
+ EXPECT_EQ(0, create_pkg_path_in_dir(path, &dir, "com.example.package", ".apk"))
+ << "Should successfully create package path.";
+
+ EXPECT_STREQ("/data/app-private/com.example.package.apk", path)
+ << "Package path should be in /data/app-private/";
+}
+
+TEST_F(UtilsTest, CreatePersonaPath_Primary) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(0, create_persona_path(path, 0))
+ << "Should successfully build primary user path.";
+
+ EXPECT_STREQ("/data/data/", path)
+ << "Primary user should have correct path";
+}
+
+TEST_F(UtilsTest, CreatePersonaPath_Secondary) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(0, create_persona_path(path, 1))
+ << "Should successfully build primary user path.";
+
+ EXPECT_STREQ("/data/user/1/", path)
+ << "Primary user should have correct path";
+}
+
+TEST_F(UtilsTest, CreateMovePath_Primary) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(0, create_move_path(path, "com.android.test", "shared_prefs", 0))
+ << "Should be able to create move path for primary user";
+
+ EXPECT_STREQ("/data/data/com.android.test/shared_prefs", path)
+ << "Primary user package directory should be created correctly";
+}
+
+TEST_F(UtilsTest, CreateMovePath_Fail_AppTooLong) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(-1, create_move_path(path, REALLY_LONG_APP_NAME, "shared_prefs", 0))
+ << "Should fail to create move path for primary user";
+}
+
+TEST_F(UtilsTest, CreateMovePath_Fail_LeafTooLong) {
+ char path[PKG_PATH_MAX];
+
+ EXPECT_EQ(-1, create_move_path(path, "com.android.test", REALLY_LONG_LEAF_NAME, 0))
+ << "Should fail to create move path for primary user";
+}
+
+TEST_F(UtilsTest, CopyAndAppend_Normal) {
+ //int copy_and_append(dir_rec_t* dst, dir_rec_t* src, char* suffix)
+ dir_rec_t dst;
+ dir_rec_t src;
+
+ src.path = "/data/";
+ src.len = strlen(src.path);
+
+ EXPECT_EQ(0, copy_and_append(&dst, &src, "app/"))
+ << "Should return error because postfix is too long.";
+
+ EXPECT_STREQ("/data/app/", dst.path)
+ << "Appended path should be correct";
+
+ EXPECT_EQ(10, (ssize_t) dst.len)
+ << "Appended path should be length of '/data/app/' (10)";
+}
+
+TEST_F(UtilsTest, AppendAndIncrement_Normal) {
+ size_t dst_size = 10;
+ char dst[dst_size];
+ char *dstp = dst;
+ const char* src = "FOO";
+
+ EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
+ << "String should append successfully";
+
+ EXPECT_STREQ("FOO", dst)
+ << "String should append correctly";
+
+ EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
+ << "String should append successfully again";
+
+ EXPECT_STREQ("FOOFOO", dst)
+ << "String should append correctly again";
+}
+
+TEST_F(UtilsTest, AppendAndIncrement_TooBig) {
+ size_t dst_size = 5;
+ char dst[dst_size];
+ char *dstp = dst;
+ const char* src = "FOO";
+
+ EXPECT_EQ(0, append_and_increment(&dstp, src, &dst_size))
+ << "String should append successfully";
+
+ EXPECT_STREQ("FOO", dst)
+ << "String should append correctly";
+
+ EXPECT_EQ(-1, append_and_increment(&dstp, src, &dst_size))
+ << "String should fail because it's too large to fit";
+}
+
+}
--- /dev/null
+/*
+** Copyright 2008, 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 "installd.h"
+
+#define CACHE_NOISY(x) //x
+
+int create_pkg_path_in_dir(char path[PKG_PATH_MAX],
+ const dir_rec_t* dir,
+ const char* pkgname,
+ const char* postfix)
+{
+ const size_t postfix_len = strlen(postfix);
+
+ const size_t pkgname_len = strlen(pkgname);
+ if (pkgname_len > PKG_NAME_MAX) {
+ return -1;
+ }
+
+ if (is_valid_package_name(pkgname) < 0) {
+ return -1;
+ }
+
+ if ((pkgname_len + dir->len + postfix_len) >= PKG_PATH_MAX) {
+ return -1;
+ }
+
+ char *dst = path;
+ size_t dst_size = PKG_PATH_MAX;
+
+ if (append_and_increment(&dst, dir->path, &dst_size) < 0
+ || append_and_increment(&dst, pkgname, &dst_size) < 0
+ || append_and_increment(&dst, postfix, &dst_size) < 0) {
+ ALOGE("Error building APK path");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Create the package path name for a given package name with a postfix for
+ * a certain persona. Returns 0 on success, and -1 on failure.
+ */
+int create_pkg_path(char path[PKG_PATH_MAX],
+ const char *pkgname,
+ const char *postfix,
+ uid_t persona)
+{
+ size_t uid_len;
+ char* persona_prefix;
+ if (persona == 0) {
+ persona_prefix = PRIMARY_USER_PREFIX;
+ uid_len = 0;
+ } else {
+ persona_prefix = SECONDARY_USER_PREFIX;
+ uid_len = snprintf(NULL, 0, "%d", persona);
+ }
+
+ const size_t prefix_len = android_data_dir.len + strlen(persona_prefix) + uid_len + 1 /*slash*/;
+ char prefix[prefix_len + 1];
+
+ char *dst = prefix;
+ size_t dst_size = sizeof(prefix);
+
+ if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0
+ || append_and_increment(&dst, persona_prefix, &dst_size) < 0) {
+ ALOGE("Error building prefix for APK path");
+ return -1;
+ }
+
+ if (persona != 0) {
+ int ret = snprintf(dst, dst_size, "%d/", persona);
+ if (ret < 0 || (size_t) ret != uid_len + 1) {
+ ALOGW("Error appending UID to APK path");
+ return -1;
+ }
+ }
+
+ dir_rec_t dir;
+ dir.path = prefix;
+ dir.len = prefix_len;
+
+ return create_pkg_path_in_dir(path, &dir, pkgname, postfix);
+}
+
+/**
+ * Create the path name for user data for a certain persona.
+ * Returns 0 on success, and -1 on failure.
+ */
+int create_persona_path(char path[PKG_PATH_MAX],
+ uid_t persona)
+{
+ size_t uid_len;
+ char* persona_prefix;
+ if (persona == 0) {
+ persona_prefix = PRIMARY_USER_PREFIX;
+ uid_len = 0;
+ } else {
+ persona_prefix = SECONDARY_USER_PREFIX;
+ uid_len = snprintf(NULL, 0, "%d/", persona);
+ }
+
+ char *dst = path;
+ size_t dst_size = PKG_PATH_MAX;
+
+ if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0
+ || append_and_increment(&dst, persona_prefix, &dst_size) < 0) {
+ ALOGE("Error building prefix for user path");
+ return -1;
+ }
+
+ if (persona != 0) {
+ if (dst_size < uid_len + 1) {
+ ALOGE("Error building user path");
+ return -1;
+ }
+ int ret = snprintf(dst, dst_size, "%d/", persona);
+ if (ret < 0 || (size_t) ret != uid_len) {
+ ALOGE("Error appending persona id to path");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Create the path name for media for a certain persona.
+ * Returns 0 on success, and -1 on failure.
+ */
+int create_persona_media_path(char path[PATH_MAX], userid_t userid) {
+ if (snprintf(path, PATH_MAX, "%s%d", android_media_dir.path, userid) > PATH_MAX) {
+ return -1;
+ }
+ return 0;
+}
+
+int create_move_path(char path[PKG_PATH_MAX],
+ const char* pkgname,
+ const char* leaf,
+ uid_t persona)
+{
+ if ((android_data_dir.len + strlen(PRIMARY_USER_PREFIX) + strlen(pkgname) + strlen(leaf) + 1)
+ >= PKG_PATH_MAX) {
+ return -1;
+ }
+
+ sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf);
+ return 0;
+}
+
+/**
+ * Checks whether the package name is valid. Returns -1 on error and
+ * 0 on success.
+ */
+int is_valid_package_name(const char* pkgname) {
+ const char *x = pkgname;
+ int alpha = -1;
+
+ while (*x) {
+ if (isalnum(*x) || (*x == '_')) {
+ /* alphanumeric or underscore are fine */
+ } else if (*x == '.') {
+ if ((x == pkgname) || (x[1] == '.') || (x[1] == 0)) {
+ /* periods must not be first, last, or doubled */
+ ALOGE("invalid package name '%s'\n", pkgname);
+ return -1;
+ }
+ } else if (*x == '-') {
+ /* Suffix -X is fine to let versioning of packages.
+ But whatever follows should be alphanumeric.*/
+ alpha = 1;
+ } else {
+ /* anything not A-Z, a-z, 0-9, _, or . is invalid */
+ ALOGE("invalid package name '%s'\n", pkgname);
+ return -1;
+ }
+
+ x++;
+ }
+
+ if (alpha == 1) {
+ // Skip current character
+ x++;
+ while (*x) {
+ if (!isalnum(*x)) {
+ ALOGE("invalid package name '%s' should include only numbers after -\n", pkgname);
+ return -1;
+ }
+ x++;
+ }
+ }
+
+ return 0;
+}
+
+static int _delete_dir_contents(DIR *d, const char *ignore)
+{
+ int result = 0;
+ struct dirent *de;
+ int dfd;
+
+ dfd = dirfd(d);
+
+ if (dfd < 0) return -1;
+
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+
+ /* skip the ignore name if provided */
+ if (ignore && !strcmp(name, ignore)) continue;
+
+ if (de->d_type == DT_DIR) {
+ int r, subfd;
+ DIR *subdir;
+
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+ if (subfd < 0) {
+ ALOGE("Couldn't openat %s: %s\n", name, strerror(errno));
+ result = -1;
+ continue;
+ }
+ subdir = fdopendir(subfd);
+ if (subdir == NULL) {
+ ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno));
+ close(subfd);
+ result = -1;
+ continue;
+ }
+ if (_delete_dir_contents(subdir, 0)) {
+ result = -1;
+ }
+ closedir(subdir);
+ if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) {
+ ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno));
+ result = -1;
+ }
+ } else {
+ if (unlinkat(dfd, name, 0) < 0) {
+ ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno));
+ result = -1;
+ }
+ }
+ }
+
+ return result;
+}
+
+int delete_dir_contents(const char *pathname,
+ int also_delete_dir,
+ const char *ignore)
+{
+ int res = 0;
+ DIR *d;
+
+ d = opendir(pathname);
+ if (d == NULL) {
+ ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno));
+ return -errno;
+ }
+ res = _delete_dir_contents(d, ignore);
+ closedir(d);
+ if (also_delete_dir) {
+ if (rmdir(pathname)) {
+ ALOGE("Couldn't rmdir %s: %s\n", pathname, strerror(errno));
+ res = -1;
+ }
+ }
+ return res;
+}
+
+int delete_dir_contents_fd(int dfd, const char *name)
+{
+ int fd, res;
+ DIR *d;
+
+ fd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+ if (fd < 0) {
+ ALOGE("Couldn't openat %s: %s\n", name, strerror(errno));
+ return -1;
+ }
+ d = fdopendir(fd);
+ if (d == NULL) {
+ ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ res = _delete_dir_contents(d, 0);
+ closedir(d);
+ return res;
+}
+
+int lookup_media_dir(char basepath[PATH_MAX], const char *dir)
+{
+ DIR *d;
+ struct dirent *de;
+ struct stat s;
+ char* dirpos = basepath + strlen(basepath);
+
+ if ((*(dirpos-1)) != '/') {
+ *dirpos = '/';
+ dirpos++;
+ }
+
+ CACHE_NOISY(ALOGI("Looking up %s in %s\n", dir, basepath));
+ // Verify the path won't extend beyond our buffer, to avoid
+ // repeated checking later.
+ if ((dirpos-basepath+strlen(dir)) >= (PATH_MAX-1)) {
+ ALOGW("Path exceeds limit: %s%s", basepath, dir);
+ return -1;
+ }
+
+ // First, can we find this directory with the case that is given?
+ strcpy(dirpos, dir);
+ if (stat(basepath, &s) >= 0) {
+ CACHE_NOISY(ALOGI("Found direct: %s\n", basepath));
+ return 0;
+ }
+
+ // Not found with that case... search through all entries to find
+ // one that matches regardless of case.
+ *dirpos = 0;
+
+ d = opendir(basepath);
+ if (d == NULL) {
+ return -1;
+ }
+
+ while ((de = readdir(d))) {
+ if (strcasecmp(de->d_name, dir) == 0) {
+ strcpy(dirpos, de->d_name);
+ closedir(d);
+ CACHE_NOISY(ALOGI("Found search: %s\n", basepath));
+ return 0;
+ }
+ }
+
+ ALOGW("Couldn't find %s in %s", dir, basepath);
+ closedir(d);
+ return -1;
+}
+
+int64_t data_disk_free()
+{
+ struct statfs sfs;
+ if (statfs(android_data_dir.path, &sfs) == 0) {
+ return sfs.f_bavail * sfs.f_bsize;
+ } else {
+ ALOGE("Couldn't statfs %s: %s\n", android_data_dir.path, strerror(errno));
+ return -1;
+ }
+}
+
+cache_t* start_cache_collection()
+{
+ cache_t* cache = (cache_t*)calloc(1, sizeof(cache_t));
+ return cache;
+}
+
+#define CACHE_BLOCK_SIZE (512*1024)
+
+static void* _cache_malloc(cache_t* cache, size_t len)
+{
+ len = (len+3)&~3;
+ if (len > (CACHE_BLOCK_SIZE/2)) {
+ // It doesn't make sense to try to put this allocation into one
+ // of our blocks, because it is so big. Instead, make a new dedicated
+ // block for it.
+ int8_t* res = (int8_t*)malloc(len+sizeof(void*));
+ if (res == NULL) {
+ return NULL;
+ }
+ CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %d", res, len));
+ // Link it into our list of blocks, not disrupting the current one.
+ if (cache->memBlocks == NULL) {
+ *(void**)res = NULL;
+ cache->memBlocks = res;
+ } else {
+ *(void**)res = *(void**)cache->memBlocks;
+ *(void**)cache->memBlocks = res;
+ }
+ return res + sizeof(void*);
+ }
+ int8_t* res = cache->curMemBlockAvail;
+ int8_t* nextPos = res + len;
+ if (cache->memBlocks == NULL || nextPos > cache->curMemBlockEnd) {
+ int8_t* newBlock = malloc(CACHE_BLOCK_SIZE);
+ if (newBlock == NULL) {
+ return NULL;
+ }
+ CACHE_NOISY(ALOGI("Allocated new cache mem block: %p", newBlock));
+ *(void**)newBlock = cache->memBlocks;
+ cache->memBlocks = newBlock;
+ res = cache->curMemBlockAvail = newBlock + sizeof(void*);
+ cache->curMemBlockEnd = newBlock + CACHE_BLOCK_SIZE;
+ nextPos = res + len;
+ }
+ CACHE_NOISY(ALOGI("cache_malloc: ret %p size %d, block=%p, nextPos=%p",
+ res, len, cache->memBlocks, nextPos));
+ cache->curMemBlockAvail = nextPos;
+ return res;
+}
+
+static void* _cache_realloc(cache_t* cache, void* cur, size_t origLen, size_t len)
+{
+ // This isn't really a realloc, but it is good enough for our purposes here.
+ void* alloc = _cache_malloc(cache, len);
+ if (alloc != NULL && cur != NULL) {
+ memcpy(alloc, cur, origLen < len ? origLen : len);
+ }
+ return alloc;
+}
+
+static void _inc_num_cache_collected(cache_t* cache)
+{
+ cache->numCollected++;
+ if ((cache->numCollected%20000) == 0) {
+ ALOGI("Collected cache so far: %d directories, %d files",
+ cache->numDirs, cache->numFiles);
+ }
+}
+
+static cache_dir_t* _add_cache_dir_t(cache_t* cache, cache_dir_t* parent, const char *name)
+{
+ size_t nameLen = strlen(name);
+ cache_dir_t* dir = (cache_dir_t*)_cache_malloc(cache, sizeof(cache_dir_t)+nameLen+1);
+ if (dir != NULL) {
+ dir->parent = parent;
+ dir->childCount = 0;
+ dir->hiddenCount = 0;
+ dir->deleted = 0;
+ strcpy(dir->name, name);
+ if (cache->numDirs >= cache->availDirs) {
+ size_t newAvail = cache->availDirs < 1000 ? 1000 : cache->availDirs*2;
+ cache_dir_t** newDirs = (cache_dir_t**)_cache_realloc(cache, cache->dirs,
+ cache->availDirs*sizeof(cache_dir_t*), newAvail*sizeof(cache_dir_t*));
+ if (newDirs == NULL) {
+ ALOGE("Failure growing cache dirs array for %s\n", name);
+ return NULL;
+ }
+ cache->availDirs = newAvail;
+ cache->dirs = newDirs;
+ }
+ cache->dirs[cache->numDirs] = dir;
+ cache->numDirs++;
+ if (parent != NULL) {
+ parent->childCount++;
+ }
+ _inc_num_cache_collected(cache);
+ } else {
+ ALOGE("Failure allocating cache_dir_t for %s\n", name);
+ }
+ return dir;
+}
+
+static cache_file_t* _add_cache_file_t(cache_t* cache, cache_dir_t* dir, time_t modTime,
+ const char *name)
+{
+ size_t nameLen = strlen(name);
+ cache_file_t* file = (cache_file_t*)_cache_malloc(cache, sizeof(cache_file_t)+nameLen+1);
+ if (file != NULL) {
+ file->dir = dir;
+ file->modTime = modTime;
+ strcpy(file->name, name);
+ if (cache->numFiles >= cache->availFiles) {
+ size_t newAvail = cache->availFiles < 1000 ? 1000 : cache->availFiles*2;
+ cache_file_t** newFiles = (cache_file_t**)_cache_realloc(cache, cache->files,
+ cache->availFiles*sizeof(cache_file_t*), newAvail*sizeof(cache_file_t*));
+ if (newFiles == NULL) {
+ ALOGE("Failure growing cache file array for %s\n", name);
+ return NULL;
+ }
+ cache->availFiles = newAvail;
+ cache->files = newFiles;
+ }
+ CACHE_NOISY(ALOGI("Setting file %p at position %d in array %p", file,
+ cache->numFiles, cache->files));
+ cache->files[cache->numFiles] = file;
+ cache->numFiles++;
+ dir->childCount++;
+ _inc_num_cache_collected(cache);
+ } else {
+ ALOGE("Failure allocating cache_file_t for %s\n", name);
+ }
+ return file;
+}
+
+static int _add_cache_files(cache_t *cache, cache_dir_t *parentDir, const char *dirName,
+ DIR* dir, char *pathBase, char *pathPos, size_t pathAvailLen)
+{
+ struct dirent *de;
+ cache_dir_t* cacheDir = NULL;
+ int dfd;
+
+ CACHE_NOISY(ALOGI("_add_cache_files: parent=%p dirName=%s dir=%p pathBase=%s",
+ parentDir, dirName, dir, pathBase));
+
+ dfd = dirfd(dir);
+
+ if (dfd < 0) return 0;
+
+ // Sub-directories always get added to the data structure, so if they
+ // are empty we will know about them to delete them later.
+ cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
+
+ while ((de = readdir(dir))) {
+ const char *name = de->d_name;
+
+ if (de->d_type == DT_DIR) {
+ int subfd;
+ DIR *subdir;
+
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+ if (subfd < 0) {
+ ALOGE("Couldn't openat %s: %s\n", name, strerror(errno));
+ continue;
+ }
+ subdir = fdopendir(subfd);
+ if (subdir == NULL) {
+ ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno));
+ close(subfd);
+ continue;
+ }
+ if (cacheDir == NULL) {
+ cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
+ }
+ if (cacheDir != NULL) {
+ // Update pathBase for the new path... this may change dirName
+ // if that is also pointing to the path, but we are done with it
+ // now.
+ size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name);
+ CACHE_NOISY(ALOGI("Collecting dir %s\n", pathBase));
+ if (finallen < pathAvailLen) {
+ _add_cache_files(cache, cacheDir, name, subdir, pathBase,
+ pathPos+finallen, pathAvailLen-finallen);
+ } else {
+ // Whoops, the final path is too long! We'll just delete
+ // this directory.
+ ALOGW("Cache dir %s truncated in path %s; deleting dir\n",
+ name, pathBase);
+ _delete_dir_contents(subdir, NULL);
+ if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) {
+ ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno));
+ }
+ }
+ }
+ closedir(subdir);
+ } else if (de->d_type == DT_REG) {
+ // Skip files that start with '.'; they will be deleted if
+ // their entire directory is deleted. This allows for metadata
+ // like ".nomedia" to remain in the directory until the entire
+ // directory is deleted.
+ if (cacheDir == NULL) {
+ cacheDir = _add_cache_dir_t(cache, parentDir, dirName);
+ }
+ if (name[0] == '.') {
+ cacheDir->hiddenCount++;
+ continue;
+ }
+ if (cacheDir != NULL) {
+ // Build final full path for file... this may change dirName
+ // if that is also pointing to the path, but we are done with it
+ // now.
+ size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name);
+ CACHE_NOISY(ALOGI("Collecting file %s\n", pathBase));
+ if (finallen < pathAvailLen) {
+ struct stat s;
+ if (stat(pathBase, &s) >= 0) {
+ _add_cache_file_t(cache, cacheDir, s.st_mtime, name);
+ } else {
+ ALOGW("Unable to stat cache file %s; deleting\n", pathBase);
+ if (unlink(pathBase) < 0) {
+ ALOGE("Couldn't unlink %s: %s\n", pathBase, strerror(errno));
+ }
+ }
+ } else {
+ // Whoops, the final path is too long! We'll just delete
+ // this file.
+ ALOGW("Cache file %s truncated in path %s; deleting\n",
+ name, pathBase);
+ if (unlinkat(dfd, name, 0) < 0) {
+ *pathPos = 0;
+ ALOGE("Couldn't unlinkat %s in %s: %s\n", name, pathBase,
+ strerror(errno));
+ }
+ }
+ }
+ } else {
+ cacheDir->hiddenCount++;
+ }
+ }
+ return 0;
+}
+
+void add_cache_files(cache_t* cache, const char *basepath, const char *cachedir)
+{
+ DIR *d;
+ struct dirent *de;
+ char dirname[PATH_MAX];
+
+ CACHE_NOISY(ALOGI("add_cache_files: base=%s cachedir=%s\n", basepath, cachedir));
+
+ d = opendir(basepath);
+ if (d == NULL) {
+ return;
+ }
+
+ while ((de = readdir(d))) {
+ if (de->d_type == DT_DIR) {
+ DIR* subdir;
+ const char *name = de->d_name;
+ char* pathpos;
+
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ strcpy(dirname, basepath);
+ pathpos = dirname + strlen(dirname);
+ if ((*(pathpos-1)) != '/') {
+ *pathpos = '/';
+ pathpos++;
+ *pathpos = 0;
+ }
+ if (cachedir != NULL) {
+ snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s/%s", name, cachedir);
+ } else {
+ snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s", name);
+ }
+ CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname));
+ subdir = opendir(dirname);
+ if (subdir != NULL) {
+ size_t dirnameLen = strlen(dirname);
+ _add_cache_files(cache, NULL, dirname, subdir, dirname, dirname+dirnameLen,
+ PATH_MAX - dirnameLen);
+ closedir(subdir);
+ }
+ }
+ }
+
+ closedir(d);
+}
+
+static char *create_dir_path(char path[PATH_MAX], cache_dir_t* dir)
+{
+ char *pos = path;
+ if (dir->parent != NULL) {
+ pos = create_dir_path(path, dir->parent);
+ }
+ // Note that we don't need to worry about going beyond the buffer,
+ // since when we were constructing the cache entries our maximum
+ // buffer size for full paths was PATH_MAX.
+ strcpy(pos, dir->name);
+ pos += strlen(pos);
+ *pos = '/';
+ pos++;
+ *pos = 0;
+ return pos;
+}
+
+static void delete_cache_dir(char path[PATH_MAX], cache_dir_t* dir)
+{
+ if (dir->parent != NULL) {
+ create_dir_path(path, dir);
+ ALOGI("DEL DIR %s\n", path);
+ if (dir->hiddenCount <= 0) {
+ if (rmdir(path)) {
+ ALOGE("Couldn't rmdir %s: %s\n", path, strerror(errno));
+ return;
+ }
+ } else {
+ // The directory contains hidden files so we need to delete
+ // them along with the directory itself.
+ if (delete_dir_contents(path, 1, NULL)) {
+ return;
+ }
+ }
+ dir->parent->childCount--;
+ dir->deleted = 1;
+ if (dir->parent->childCount <= 0) {
+ delete_cache_dir(path, dir->parent);
+ }
+ } else if (dir->hiddenCount > 0) {
+ // This is a root directory, but it has hidden files. Get rid of
+ // all of those files, but not the directory itself.
+ create_dir_path(path, dir);
+ ALOGI("DEL CONTENTS %s\n", path);
+ delete_dir_contents(path, 0, NULL);
+ }
+}
+
+static int cache_modtime_sort(const void *lhsP, const void *rhsP)
+{
+ const cache_file_t *lhs = *(const cache_file_t**)lhsP;
+ const cache_file_t *rhs = *(const cache_file_t**)rhsP;
+ return lhs->modTime < rhs->modTime ? -1 : (lhs->modTime > rhs->modTime ? 1 : 0);
+}
+
+void clear_cache_files(cache_t* cache, int64_t free_size)
+{
+ size_t i;
+ int skip = 0;
+ char path[PATH_MAX];
+
+ ALOGI("Collected cache files: %d directories, %d files",
+ cache->numDirs, cache->numFiles);
+
+ CACHE_NOISY(ALOGI("Sorting files..."));
+ qsort(cache->files, cache->numFiles, sizeof(cache_file_t*),
+ cache_modtime_sort);
+
+ CACHE_NOISY(ALOGI("Cleaning empty directories..."));
+ for (i=cache->numDirs; i>0; i--) {
+ cache_dir_t* dir = cache->dirs[i-1];
+ if (dir->childCount <= 0 && !dir->deleted) {
+ delete_cache_dir(path, dir);
+ }
+ }
+
+ CACHE_NOISY(ALOGI("Trimming files..."));
+ for (i=0; i<cache->numFiles; i++) {
+ skip++;
+ if (skip > 10) {
+ if (data_disk_free() > free_size) {
+ return;
+ }
+ skip = 0;
+ }
+ cache_file_t* file = cache->files[i];
+ strcpy(create_dir_path(path, file->dir), file->name);
+ ALOGI("DEL (mod %d) %s\n", (int)file->modTime, path);
+ if (unlink(path) < 0) {
+ ALOGE("Couldn't unlink %s: %s\n", path, strerror(errno));
+ }
+ file->dir->childCount--;
+ if (file->dir->childCount <= 0) {
+ delete_cache_dir(path, file->dir);
+ }
+ }
+}
+
+void finish_cache_collection(cache_t* cache)
+{
+ size_t i;
+
+ CACHE_NOISY(ALOGI("clear_cache_files: %d dirs, %d files\n", cache->numDirs, cache->numFiles));
+ CACHE_NOISY(
+ for (i=0; i<cache->numDirs; i++) {
+ cache_dir_t* dir = cache->dirs[i];
+ ALOGI("dir #%d: %p %s parent=%p\n", i, dir, dir->name, dir->parent);
+ })
+ CACHE_NOISY(
+ for (i=0; i<cache->numFiles; i++) {
+ cache_file_t* file = cache->files[i];
+ ALOGI("file #%d: %p %s time=%d dir=%p\n", i, file, file->name,
+ (int)file->modTime, file->dir);
+ })
+ void* block = cache->memBlocks;
+ while (block != NULL) {
+ void* nextBlock = *(void**)block;
+ CACHE_NOISY(ALOGI("Freeing cache mem block: %p", block));
+ free(block);
+ block = nextBlock;
+ }
+ free(cache);
+}
+
+/**
+ * Checks whether a path points to a system app (.apk file). Returns 0
+ * if it is a system app or -1 if it is not.
+ */
+int validate_system_app_path(const char* path) {
+ size_t i;
+
+ for (i = 0; i < android_system_dirs.count; i++) {
+ const size_t dir_len = android_system_dirs.dirs[i].len;
+ if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) {
+ if (path[dir_len] == '.' || strchr(path + dir_len, '/') != NULL) {
+ ALOGE("invalid system apk path '%s' (trickery)\n", path);
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Get the contents of a environment variable that contains a path. Caller
+ * owns the string that is inserted into the directory record. Returns
+ * 0 on success and -1 on error.
+ */
+int get_path_from_env(dir_rec_t* rec, const char* var) {
+ const char* path = getenv(var);
+ int ret = get_path_from_string(rec, path);
+ if (ret < 0) {
+ ALOGW("Problem finding value for environment variable %s\n", var);
+ }
+ return ret;
+}
+
+/**
+ * Puts the string into the record as a directory. Appends '/' to the end
+ * of all paths. Caller owns the string that is inserted into the directory
+ * record. A null value will result in an error.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int get_path_from_string(dir_rec_t* rec, const char* path) {
+ if (path == NULL) {
+ return -1;
+ } else {
+ const size_t path_len = strlen(path);
+ if (path_len <= 0) {
+ return -1;
+ }
+
+ // Make sure path is absolute.
+ if (path[0] != '/') {
+ return -1;
+ }
+
+ if (path[path_len - 1] == '/') {
+ // Path ends with a forward slash. Make our own copy.
+
+ rec->path = strdup(path);
+ if (rec->path == NULL) {
+ return -1;
+ }
+
+ rec->len = path_len;
+ } else {
+ // Path does not end with a slash. Generate a new string.
+ char *dst;
+
+ // Add space for slash and terminating null.
+ size_t dst_size = path_len + 2;
+
+ rec->path = malloc(dst_size);
+ if (rec->path == NULL) {
+ return -1;
+ }
+
+ dst = rec->path;
+
+ if (append_and_increment(&dst, path, &dst_size) < 0
+ || append_and_increment(&dst, "/", &dst_size)) {
+ ALOGE("Error canonicalizing path");
+ return -1;
+ }
+
+ rec->len = dst - rec->path;
+ }
+ }
+ return 0;
+}
+
+int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) {
+ dst->len = src->len + strlen(suffix);
+ const size_t dstSize = dst->len + 1;
+ dst->path = (char*) malloc(dstSize);
+
+ if (dst->path == NULL
+ || snprintf(dst->path, dstSize, "%s%s", src->path, suffix)
+ != (ssize_t) dst->len) {
+ ALOGE("Could not allocate memory to hold appended path; aborting\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Check whether path points to a valid path for an APK file. An ASEC
+ * directory is allowed to have one level of subdirectory names. Returns -1
+ * when an invalid path is encountered and 0 when a valid path is encountered.
+ */
+int validate_apk_path(const char *path)
+{
+ int allowsubdir = 0;
+ char *subdir = NULL;
+ size_t dir_len;
+ size_t path_len;
+
+ if (!strncmp(path, android_app_dir.path, android_app_dir.len)) {
+ dir_len = android_app_dir.len;
+ } else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) {
+ dir_len = android_app_private_dir.len;
+ } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) {
+ dir_len = android_asec_dir.len;
+ allowsubdir = 1;
+ } else {
+ ALOGE("invalid apk path '%s' (bad prefix)\n", path);
+ return -1;
+ }
+
+ path_len = strlen(path);
+
+ /*
+ * Only allow the path to have a subdirectory if it's been marked as being allowed.
+ */
+ if ((subdir = strchr(path + dir_len, '/')) != NULL) {
+ ++subdir;
+ if (!allowsubdir
+ || (path_len > (size_t) (subdir - path) && (strchr(subdir, '/') != NULL))) {
+ ALOGE("invalid apk path '%s' (subdir?)\n", path);
+ return -1;
+ }
+ }
+
+ /*
+ * Directories can't have a period directly after the directory markers
+ * to prevent ".."
+ */
+ if (path[dir_len] == '.'
+ || (subdir != NULL && ((*subdir == '.') || (strchr(subdir, '/') != NULL)))) {
+ ALOGE("invalid apk path '%s' (trickery)\n", path);
+ return -1;
+ }
+
+ return 0;
+}
+
+int append_and_increment(char** dst, const char* src, size_t* dst_size) {
+ ssize_t ret = strlcpy(*dst, src, *dst_size);
+ if (ret < 0 || (size_t) ret >= *dst_size) {
+ return -1;
+ }
+ *dst += ret;
+ *dst_size -= ret;
+ return 0;
+}
+
+char *build_string2(char *s1, char *s2) {
+ if (s1 == NULL || s2 == NULL) return NULL;
+
+ int len_s1 = strlen(s1);
+ int len_s2 = strlen(s2);
+ int len = len_s1 + len_s2 + 1;
+ char *result = malloc(len);
+ if (result == NULL) return NULL;
+
+ strcpy(result, s1);
+ strcpy(result + len_s1, s2);
+
+ return result;
+}
+
+char *build_string3(char *s1, char *s2, char *s3) {
+ if (s1 == NULL || s2 == NULL || s3 == NULL) return NULL;
+
+ int len_s1 = strlen(s1);
+ int len_s2 = strlen(s2);
+ int len_s3 = strlen(s3);
+ int len = len_s1 + len_s2 + len_s3 + 1;
+ char *result = malloc(len);
+ if (result == NULL) return NULL;
+
+ strcpy(result, s1);
+ strcpy(result + len_s1, s2);
+ strcpy(result + len_s1 + len_s2, s3);
+
+ return result;
+}
+
+/* Ensure that /data/media directories are prepared for given user. */
+int ensure_media_user_dirs(userid_t userid) {
+ char media_user_path[PATH_MAX];
+ char path[PATH_MAX];
+
+ // Ensure /data/media/<userid> exists
+ create_persona_media_path(media_user_path, userid);
+ if (fs_prepare_dir(media_user_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := ip-up-vpn.c
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_MODULE := ip-up-vpn
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
--- /dev/null
+/*
+ * Copyright (C) 2011 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/route.h>
+
+#define LOG_TAG "ip-up-vpn"
+#include <cutils/log.h>
+
+#define DIR "/data/misc/vpn/"
+
+static const char *env(const char *name) {
+ const char *value = getenv(name);
+ return value ? value : "";
+}
+
+static int set_address(struct sockaddr *sa, const char *address) {
+ sa->sa_family = AF_INET;
+ errno = EINVAL;
+ return inet_pton(AF_INET, address, &((struct sockaddr_in *)sa)->sin_addr);
+}
+
+/*
+ * The primary goal is to create a file with VPN parameters. Currently they
+ * are interface, addresses, routes, DNS servers, and search domains. Each
+ * parameter occupies one line in the file, and it can be an empty string or
+ * space-separated values. The order and the format must be consistent with
+ * com.android.server.connectivity.Vpn. Here is an example.
+ *
+ * ppp0
+ * 192.168.1.100/24
+ * 0.0.0.0/0
+ * 192.168.1.1 192.168.1.2
+ * example.org
+ *
+ * The secondary goal is to unify the outcome of VPN. The current baseline
+ * is to have an interface configured with the given address and netmask
+ * and maybe add a host route to protect the tunnel. PPP-based VPN already
+ * does this, but others might not. Routes, DNS servers, and search domains
+ * are handled by the framework since they can be overridden by the users.
+ */
+int main(int argc, char **argv)
+{
+ FILE *state = fopen(DIR ".tmp", "wb");
+ if (!state) {
+ ALOGE("Cannot create state: %s", strerror(errno));
+ return 1;
+ }
+
+ if (argc >= 6) {
+ /* Invoked by pppd. */
+ fprintf(state, "%s\n", argv[1]);
+ fprintf(state, "%s/32\n", argv[4]);
+ fprintf(state, "0.0.0.0/0\n");
+ fprintf(state, "%s %s\n", env("DNS1"), env("DNS2"));
+ fprintf(state, "\n");
+ } else if (argc == 2) {
+ /* Invoked by racoon. */
+ const char *interface = env("INTERFACE");
+ const char *address = env("INTERNAL_ADDR4");
+ const char *routes = env("SPLIT_INCLUDE_CIDR");
+
+ int s = socket(AF_INET, SOCK_DGRAM, 0);
+ struct rtentry rt;
+ struct ifreq ifr;
+
+ memset(&rt, 0, sizeof(rt));
+ memset(&ifr, 0, sizeof(ifr));
+
+ /* Remove the old host route. There could be more than one. */
+ rt.rt_flags |= RTF_UP | RTF_HOST;
+ if (set_address(&rt.rt_dst, env("REMOTE_ADDR"))) {
+ while (!ioctl(s, SIOCDELRT, &rt));
+ }
+ if (errno != ESRCH) {
+ ALOGE("Cannot remove host route: %s", strerror(errno));
+ return 1;
+ }
+
+ /* Create a new host route. */
+ rt.rt_flags |= RTF_GATEWAY;
+ if (!set_address(&rt.rt_gateway, argv[1]) ||
+ (ioctl(s, SIOCADDRT, &rt) && errno != EEXIST)) {
+ ALOGE("Cannot create host route: %s", strerror(errno));
+ return 1;
+ }
+
+ /* Bring up the interface. */
+ ifr.ifr_flags = IFF_UP;
+ strncpy(ifr.ifr_name, interface, IFNAMSIZ);
+ if (ioctl(s, SIOCSIFFLAGS, &ifr)) {
+ ALOGE("Cannot bring up %s: %s", interface, strerror(errno));
+ return 1;
+ }
+
+ /* Set the address. */
+ if (!set_address(&ifr.ifr_addr, address) ||
+ ioctl(s, SIOCSIFADDR, &ifr)) {
+ ALOGE("Cannot set address: %s", strerror(errno));
+ return 1;
+ }
+
+ /* Set the netmask. */
+ if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) {
+ if (ioctl(s, SIOCSIFNETMASK, &ifr)) {
+ ALOGE("Cannot set netmask: %s", strerror(errno));
+ return 1;
+ }
+ }
+
+ /* TODO: Send few packets to trigger phase 2? */
+
+ fprintf(state, "%s\n", interface);
+ fprintf(state, "%s/%s\n", address, env("INTERNAL_CIDR4"));
+ fprintf(state, "%s\n", routes[0] ? routes : "0.0.0.0/0");
+ fprintf(state, "%s\n", env("INTERNAL_DNS4_LIST"));
+ fprintf(state, "%s\n", env("DEFAULT_DOMAIN"));
+ } else {
+ ALOGE("Cannot parse parameters");
+ return 1;
+ }
+
+ fclose(state);
+ if (chmod(DIR ".tmp", 0444) || rename(DIR ".tmp", DIR "state")) {
+ ALOGE("Cannot write state: %s", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+# Copyright 2009 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= backup.cpp
+
+LOCAL_SHARED_LIBRARIES := libcutils libc
+
+LOCAL_MODULE:= rawbu
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_EXECUTABLE)
--- /dev/null
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
--- /dev/null
+// Copyright 2009 The Android Open Source Project
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <dirent.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <cutils/properties.h>
+
+#include <private/android_filesystem_config.h>
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+// First version.
+#define FILE_VERSION_1 0xffff0001
+
+// Introduces backup all option to header.
+#define FILE_VERSION_2 0xffff0002
+
+#define FILE_VERSION FILE_VERSION_2
+
+namespace android {
+
+static char nameBuffer[PATH_MAX];
+static struct stat statBuffer;
+
+static char copyBuffer[8192];
+static char *backupFilePath = NULL;
+
+static uint32_t inputFileVersion;
+
+static int opt_backupAll;
+
+#define SPECIAL_NO_TOUCH 0
+#define SPECIAL_NO_BACKUP 1
+
+struct special_dir {
+ const char* path;
+ int type;
+};
+
+/* Directory paths that we will not backup/restore */
+static const struct special_dir SKIP_PATHS[] = {
+ { "/data/misc", SPECIAL_NO_TOUCH },
+ { "/data/system/batterystats.bin", SPECIAL_NO_TOUCH },
+ { "/data/system/location", SPECIAL_NO_TOUCH },
+ { "/data/dalvik-cache", SPECIAL_NO_BACKUP },
+ { NULL, 0 },
+};
+
+/* This is just copied from the shell's built-in wipe command. */
+static int wipe (const char *path)
+{
+ DIR *dir;
+ struct dirent *de;
+ int ret;
+ int i;
+
+ dir = opendir(path);
+
+ if (dir == NULL) {
+ fprintf (stderr, "Error opendir'ing %s: %s\n",
+ path, strerror(errno));
+ return 0;
+ }
+
+ char *filenameOffset;
+
+ strcpy(nameBuffer, path);
+ strcat(nameBuffer, "/");
+
+ filenameOffset = nameBuffer + strlen(nameBuffer);
+
+ for (;;) {
+ de = readdir(dir);
+
+ if (de == NULL) {
+ break;
+ }
+
+ if (0 == strcmp(de->d_name, ".")
+ || 0 == strcmp(de->d_name, "..")
+ || 0 == strcmp(de->d_name, "lost+found")
+ ) {
+ continue;
+ }
+
+ strcpy(filenameOffset, de->d_name);
+ bool noBackup = false;
+
+ /* See if this is a path we should skip. */
+ for (i = 0; SKIP_PATHS[i].path; i++) {
+ if (strcmp(SKIP_PATHS[i].path, nameBuffer) == 0) {
+ if (opt_backupAll || SKIP_PATHS[i].type == SPECIAL_NO_BACKUP) {
+ // In this case we didn't back up the directory --
+ // we do want to wipe its contents, but not the
+ // directory itself, since the restore file won't
+ // contain the directory.
+ noBackup = true;
+ }
+ break;
+ }
+ }
+
+ if (!noBackup && SKIP_PATHS[i].path != NULL) {
+ // This is a SPECIAL_NO_TOUCH directory.
+ continue;
+ }
+
+ ret = lstat (nameBuffer, &statBuffer);
+
+ if (ret != 0) {
+ fprintf(stderr, "warning -- stat() error on '%s': %s\n",
+ nameBuffer, strerror(errno));
+ continue;
+ }
+
+ if(S_ISDIR(statBuffer.st_mode)) {
+ int i;
+ char *newpath;
+
+ newpath = strdup(nameBuffer);
+ if (wipe(newpath) == 0) {
+ free(newpath);
+ closedir(dir);
+ return 0;
+ }
+
+ if (!noBackup) {
+ ret = rmdir(newpath);
+ if (ret != 0) {
+ fprintf(stderr, "warning -- rmdir() error on '%s': %s\n",
+ newpath, strerror(errno));
+ }
+ }
+
+ free(newpath);
+
+ strcpy(nameBuffer, path);
+ strcat(nameBuffer, "/");
+
+ } else {
+ // Don't delete the backup file
+ if (backupFilePath && strcmp(backupFilePath, nameBuffer) == 0) {
+ continue;
+ }
+ ret = unlink(nameBuffer);
+
+ if (ret != 0) {
+ fprintf(stderr, "warning -- unlink() error on '%s': %s\n",
+ nameBuffer, strerror(errno));
+ }
+ }
+ }
+
+ closedir(dir);
+
+ return 1;
+}
+
+static int write_int32(FILE* fh, int32_t val)
+{
+ int res = fwrite(&val, 1, sizeof(val), fh);
+ if (res != sizeof(val)) {
+ fprintf(stderr, "unable to write int32 (%d bytes): %s\n", res, strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int write_int64(FILE* fh, int64_t val)
+{
+ int res = fwrite(&val, 1, sizeof(val), fh);
+ if (res != sizeof(val)) {
+ fprintf(stderr, "unable to write int64 (%d bytes): %s\n", res, strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int copy_file(FILE* dest, FILE* src, off_t size, const char* destName,
+ const char* srcName)
+{
+ errno = 0;
+
+ off_t origSize = size;
+
+ while (size > 0) {
+ int amt = size > (off_t)sizeof(copyBuffer) ? sizeof(copyBuffer) : (int)size;
+ int readLen = fread(copyBuffer, 1, amt, src);
+ if (readLen <= 0) {
+ if (srcName != NULL) {
+ fprintf(stderr, "unable to read source (%d of %ld bytes) file '%s': %s\n",
+ amt, origSize, srcName, errno != 0 ? strerror(errno) : "unexpected EOF");
+ } else {
+ fprintf(stderr, "unable to read buffer (%d of %ld bytes): %s\n",
+ amt, origSize, errno != 0 ? strerror(errno) : "unexpected EOF");
+ }
+ return 0;
+ }
+ int writeLen = fwrite(copyBuffer, 1, readLen, dest);
+ if (writeLen != readLen) {
+ if (destName != NULL) {
+ fprintf(stderr, "unable to write file (%d of %d bytes) '%s': '%s'\n",
+ writeLen, readLen, destName, strerror(errno));
+ } else {
+ fprintf(stderr, "unable to write buffer (%d of %d bytes): '%s'\n",
+ writeLen, readLen, strerror(errno));
+ }
+ return 0;
+ }
+ size -= readLen;
+ }
+ return 1;
+}
+
+#define TYPE_END 0
+#define TYPE_DIR 1
+#define TYPE_FILE 2
+
+static int write_header(FILE* fh, int type, const char* path, const struct stat* st)
+{
+ int pathLen = strlen(path);
+ if (!write_int32(fh, type)) return 0;
+ if (!write_int32(fh, pathLen)) return 0;
+ if (fwrite(path, 1, pathLen, fh) != (size_t)pathLen) {
+ fprintf(stderr, "unable to write: %s\n", strerror(errno));
+ return 0;
+ }
+
+ if (!write_int32(fh, st->st_uid)) return 0;
+ if (!write_int32(fh, st->st_gid)) return 0;
+ if (!write_int32(fh, st->st_mode)) return 0;
+ if (!write_int64(fh, ((int64_t)st->st_atime)*1000*1000*1000)) return 0;
+ if (!write_int64(fh, ((int64_t)st->st_mtime)*1000*1000*1000)) return 0;
+ if (!write_int64(fh, ((int64_t)st->st_ctime)*1000*1000*1000)) return 0;
+
+ return 1;
+}
+
+static int backup_dir(FILE* fh, const char* srcPath)
+{
+ DIR *dir;
+ struct dirent *de;
+ char* fullPath = NULL;
+ int srcLen = strlen(srcPath);
+ int result = 1;
+ int i;
+
+ dir = opendir(srcPath);
+
+ if (dir == NULL) {
+ fprintf (stderr, "error opendir'ing '%s': %s\n",
+ srcPath, strerror(errno));
+ return 0;
+ }
+
+ for (;;) {
+ de = readdir(dir);
+
+ if (de == NULL) {
+ break;
+ }
+
+ if (0 == strcmp(de->d_name, ".")
+ || 0 == strcmp(de->d_name, "..")
+ || 0 == strcmp(de->d_name, "lost+found")
+ ) {
+ continue;
+ }
+
+ if (fullPath != NULL) {
+ free(fullPath);
+ }
+ fullPath = (char*)malloc(srcLen + strlen(de->d_name) + 2);
+ strcpy(fullPath, srcPath);
+ fullPath[srcLen] = '/';
+ strcpy(fullPath+srcLen+1, de->d_name);
+
+ /* See if this is a path we should skip. */
+ if (!opt_backupAll) {
+ for (i = 0; SKIP_PATHS[i].path; i++) {
+ if (strcmp(SKIP_PATHS[i].path, fullPath) == 0) {
+ break;
+ }
+ }
+ if (SKIP_PATHS[i].path != NULL) {
+ continue;
+ }
+ }
+
+ int ret = lstat(fullPath, &statBuffer);
+
+ if (ret != 0) {
+ fprintf(stderr, "stat() error on '%s': %s\n",
+ fullPath, strerror(errno));
+ result = 0;
+ goto done;
+ }
+
+ if(S_ISDIR(statBuffer.st_mode)) {
+ printf("Saving dir %s...\n", fullPath);
+
+ if (write_header(fh, TYPE_DIR, fullPath, &statBuffer) == 0) {
+ result = 0;
+ goto done;
+ }
+ if (backup_dir(fh, fullPath) == 0) {
+ result = 0;
+ goto done;
+ }
+ } else if (S_ISREG(statBuffer.st_mode)) {
+ // Skip the backup file
+ if (backupFilePath && strcmp(fullPath, backupFilePath) == 0) {
+ printf("Skipping backup file %s...\n", backupFilePath);
+ continue;
+ } else {
+ printf("Saving file %s...\n", fullPath);
+ }
+ if (write_header(fh, TYPE_FILE, fullPath, &statBuffer) == 0) {
+ result = 0;
+ goto done;
+ }
+
+ off_t size = statBuffer.st_size;
+ if (!write_int64(fh, size)) {
+ result = 0;
+ goto done;
+ }
+
+ FILE* src = fopen(fullPath, "r");
+ if (src == NULL) {
+ fprintf(stderr, "unable to open source file '%s': %s\n",
+ fullPath, strerror(errno));
+ result = 0;
+ goto done;
+ }
+
+ int copyres = copy_file(fh, src, size, NULL, fullPath);
+ fclose(src);
+ if (!copyres) {
+ result = 0;
+ goto done;
+ }
+ }
+ }
+
+done:
+ if (fullPath != NULL) {
+ free(fullPath);
+ }
+
+ closedir(dir);
+
+ return result;
+}
+
+static int backup_data(const char* destPath)
+{
+ int res = -1;
+
+ FILE* fh = fopen(destPath, "w");
+ if (fh == NULL) {
+ fprintf(stderr, "unable to open destination '%s': %s\n",
+ destPath, strerror(errno));
+ return -1;
+ }
+
+ printf("Backing up /data to %s...\n", destPath);
+
+ // The path that shouldn't be backed up
+ backupFilePath = strdup(destPath);
+
+ if (!write_int32(fh, FILE_VERSION)) goto done;
+ if (!write_int32(fh, opt_backupAll)) goto done;
+ if (!backup_dir(fh, "/data")) goto done;
+ if (!write_int32(fh, 0)) goto done;
+
+ res = 0;
+
+done:
+ if (fflush(fh) != 0) {
+ fprintf(stderr, "error flushing destination '%s': %s\n",
+ destPath, strerror(errno));
+ res = -1;
+ goto donedone;
+ }
+ if (fsync(fileno(fh)) != 0) {
+ fprintf(stderr, "error syncing destination '%s': %s\n",
+ destPath, strerror(errno));
+ res = -1;
+ goto donedone;
+ }
+ fclose(fh);
+ sync();
+
+donedone:
+ return res;
+}
+
+static int32_t read_int32(FILE* fh, int32_t defVal)
+{
+ int32_t val;
+ if (fread(&val, 1, sizeof(val), fh) != sizeof(val)) {
+ fprintf(stderr, "unable to read: %s\n", strerror(errno));
+ return defVal;
+ }
+
+ return val;
+}
+
+static int64_t read_int64(FILE* fh, int64_t defVal)
+{
+ int64_t val;
+ if (fread(&val, 1, sizeof(val), fh) != sizeof(val)) {
+ fprintf(stderr, "unable to read: %s\n", strerror(errno));
+ return defVal;
+ }
+
+ return val;
+}
+
+static int read_header(FILE* fh, int* type, char** path, struct stat* st)
+{
+ *type = read_int32(fh, -1);
+ if (*type == TYPE_END) {
+ return 1;
+ }
+
+ if (*type < 0) {
+ fprintf(stderr, "bad token %d in restore file\n", *type);
+ return 0;
+ }
+
+ int32_t pathLen = read_int32(fh, -1);
+ if (pathLen <= 0) {
+ fprintf(stderr, "bad path length %d in restore file\n", pathLen);
+ return 0;
+ }
+ char* readPath = (char*)malloc(pathLen+1);
+ if (fread(readPath, 1, pathLen, fh) != (size_t)pathLen) {
+ fprintf(stderr, "truncated path in restore file\n");
+ free(readPath);
+ return 0;
+ }
+ readPath[pathLen] = 0;
+ *path = readPath;
+
+ st->st_uid = read_int32(fh, -1);
+ if (st->st_uid == (uid_t)-1) {
+ fprintf(stderr, "bad uid in restore file at '%s'\n", readPath);
+ return 0;
+ }
+ st->st_gid = read_int32(fh, -1);
+ if (st->st_gid == (gid_t)-1) {
+ fprintf(stderr, "bad gid in restore file at '%s'\n", readPath);
+ return 0;
+ }
+ st->st_mode = read_int32(fh, -1);
+ if (st->st_mode == (mode_t)-1) {
+ fprintf(stderr, "bad mode in restore file at '%s'\n", readPath);
+ return 0;
+ }
+ int64_t ltime = read_int64(fh, -1);
+ if (ltime < 0) {
+ fprintf(stderr, "bad atime in restore file at '%s'\n", readPath);
+ return 0;
+ }
+ st->st_atime = (time_t)(ltime/1000/1000/1000);
+ ltime = read_int64(fh, -1);
+ if (ltime < 0) {
+ fprintf(stderr, "bad mtime in restore file at '%s'\n", readPath);
+ return 0;
+ }
+ st->st_mtime = (time_t)(ltime/1000/1000/1000);
+ ltime = read_int64(fh, -1);
+ if (ltime < 0) {
+ fprintf(stderr, "bad ctime in restore file at '%s'\n", readPath);
+ return 0;
+ }
+ st->st_ctime = (time_t)(ltime/1000/1000/1000);
+
+ st->st_mode &= (S_IRWXU|S_IRWXG|S_IRWXO);
+
+ return 1;
+}
+
+static int restore_data(const char* srcPath)
+{
+ int res = -1;
+
+ FILE* fh = fopen(srcPath, "r");
+ if (fh == NULL) {
+ fprintf(stderr, "Unable to open source '%s': %s\n",
+ srcPath, strerror(errno));
+ return -1;
+ }
+
+ inputFileVersion = read_int32(fh, 0);
+ if (inputFileVersion < FILE_VERSION_1 || inputFileVersion > FILE_VERSION) {
+ fprintf(stderr, "Restore file has bad version: 0x%x\n", inputFileVersion);
+ goto done;
+ }
+
+ if (inputFileVersion >= FILE_VERSION_2) {
+ opt_backupAll = read_int32(fh, 0);
+ } else {
+ opt_backupAll = 0;
+ }
+
+ // The path that shouldn't be deleted
+ backupFilePath = strdup(srcPath);
+
+ printf("Wiping contents of /data...\n");
+ if (!wipe("/data")) {
+ goto done;
+ }
+
+ printf("Restoring from %s to /data...\n", srcPath);
+
+ while (1) {
+ int type;
+ char* path = NULL;
+ if (read_header(fh, &type, &path, &statBuffer) == 0) {
+ goto done;
+ }
+ if (type == 0) {
+ break;
+ }
+
+ const char* typeName = "?";
+
+ if (type == TYPE_DIR) {
+ typeName = "dir";
+
+ printf("Restoring dir %s...\n", path);
+
+ if (mkdir(path, statBuffer.st_mode) != 0) {
+ if (errno != EEXIST) {
+ fprintf(stderr, "unable to create directory '%s': %s\n",
+ path, strerror(errno));
+ free(path);
+ goto done;
+ }
+ }
+
+ } else if (type == TYPE_FILE) {
+ typeName = "file";
+ off_t size = read_int64(fh, -1);
+ if (size < 0) {
+ fprintf(stderr, "bad file size %ld in restore file\n", size);
+ free(path);
+ goto done;
+ }
+
+ printf("Restoring file %s...\n", path);
+
+ FILE* dest = fopen(path, "w");
+ if (dest == NULL) {
+ fprintf(stderr, "unable to open destination file '%s': %s\n",
+ path, strerror(errno));
+ free(path);
+ goto done;
+ }
+
+ int copyres = copy_file(dest, fh, size, path, NULL);
+ fclose(dest);
+ if (!copyres) {
+ free(path);
+ goto done;
+ }
+
+ } else {
+ fprintf(stderr, "unknown node type %d\n", type);
+ goto done;
+ }
+
+ // Do this even for directories, since the dir may have already existed
+ // so we need to make sure it gets the correct mode.
+ if (chmod(path, statBuffer.st_mode&(S_IRWXU|S_IRWXG|S_IRWXO)) != 0) {
+ fprintf(stderr, "unable to chmod destination %s '%s' to 0x%x: %s\n",
+ typeName, path, statBuffer.st_mode, strerror(errno));
+ free(path);
+ goto done;
+ }
+
+ if (chown(path, statBuffer.st_uid, statBuffer.st_gid) != 0) {
+ fprintf(stderr, "unable to chown destination %s '%s' to uid %d / gid %d: %s\n",
+ typeName, path, (int)statBuffer.st_uid, (int)statBuffer.st_gid, strerror(errno));
+ free(path);
+ goto done;
+ }
+
+ struct utimbuf timbuf;
+ timbuf.actime = statBuffer.st_atime;
+ timbuf.modtime = statBuffer.st_mtime;
+ if (utime(path, &timbuf) != 0) {
+ fprintf(stderr, "unable to utime destination %s '%s': %s\n",
+ typeName, path, strerror(errno));
+ free(path);
+ goto done;
+ }
+
+
+ free(path);
+ }
+
+ res = 0;
+
+done:
+ fclose(fh);
+
+ return res;
+}
+
+static void show_help(const char *cmd)
+{
+ fprintf(stderr,"Usage: %s COMMAND [options] [backup-file-path]\n", cmd);
+
+ fprintf(stderr, "commands are:\n"
+ " help Show this help text.\n"
+ " backup Perform a backup of /data.\n"
+ " restore Perform a restore of /data.\n");
+ fprintf(stderr, "options include:\n"
+ " -h Show this help text.\n"
+ " -a Backup all files.\n");
+ fprintf(stderr, "\nThe %s command allows you to perform low-level\n"
+ "backup and restore of the /data partition. This is\n"
+ "where all user data is kept, allowing for a fairly\n"
+ "complete restore of a device's state. Note that\n"
+ "because this is low-level, it will only work across\n"
+ "builds of the same (or very similar) device software.\n",
+ cmd);
+}
+
+} /* namespace android */
+
+int main (int argc, char **argv)
+{
+ int restore = 0;
+
+ if (getuid() != AID_ROOT) {
+ fprintf(stderr, "error -- %s must run as root\n", argv[0]);
+ exit(-1);
+ }
+
+ if (argc < 2) {
+ fprintf(stderr, "No command specified.\n");
+ android::show_help(argv[0]);
+ exit(-1);
+ }
+
+ if (0 == strcmp(argv[1], "restore")) {
+ restore = 1;
+ } else if (0 == strcmp(argv[1], "help")) {
+ android::show_help(argv[0]);
+ exit(0);
+ } else if (0 != strcmp(argv[1], "backup")) {
+ fprintf(stderr, "Unknown command: %s\n", argv[1]);
+ android::show_help(argv[0]);
+ exit(-1);
+ }
+
+ android::opt_backupAll = 0;
+
+ optind = 2;
+
+ for (;;) {
+ int ret;
+
+ ret = getopt(argc, argv, "ah");
+
+ if (ret < 0) {
+ break;
+ }
+
+ switch(ret) {
+ case 'a':
+ android::opt_backupAll = 1;
+ if (restore) fprintf(stderr, "Warning: -a option ignored on restore\n");
+ break;
+ case 'h':
+ android::show_help(argv[0]);
+ exit(0);
+ break;
+
+ default:
+ fprintf(stderr,"Unrecognized Option\n");
+ android::show_help(argv[0]);
+ exit(-1);
+ break;
+ }
+ }
+
+ const char* backupFile = "/sdcard/backup.dat";
+
+ if (argc > optind) {
+ backupFile = argv[optind];
+ optind++;
+ if (argc != optind) {
+ fprintf(stderr, "Too many arguments\n");
+ android::show_help(argv[0]);
+ exit(-1);
+ }
+ }
+
+ printf("Stopping system...\n");
+ property_set("ctl.stop", "runtime");
+ property_set("ctl.stop", "zygote");
+ sleep(1);
+
+ int res;
+ if (restore) {
+ res = android::restore_data(backupFile);
+ if (res != 0) {
+ // Don't restart system, since the data partition is hosed.
+ return res;
+ }
+ printf("Restore complete! Restarting system, cross your fingers...\n");
+ } else {
+ res = android::backup_data(backupFile);
+ if (res == 0) {
+ printf("Backup complete! Restarting system...\n");
+ } else {
+ printf("Restarting system...\n");
+ }
+ }
+
+ property_set("ctl.start", "zygote");
+ property_set("ctl.start", "runtime");
+}
--- /dev/null
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := screenshot.c
+
+LOCAL_MODULE := screenshot
+
+LOCAL_SHARED_LIBRARIES := libcutils libz
+LOCAL_STATIC_LIBRARIES := libpng
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_EXECUTABLE)
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <linux/fb.h>
+
+#include <zlib.h>
+#include <libpng/png.h>
+
+#include "private/android_filesystem_config.h"
+
+#define LOG_TAG "screenshot"
+#include <utils/Log.h>
+
+void take_screenshot(FILE *fb_in, FILE *fb_out) {
+ int fb;
+ char imgbuf[0x10000];
+ struct fb_var_screeninfo vinfo;
+ png_structp png;
+ png_infop info;
+ unsigned int r,c,rowlen;
+ unsigned int bytespp,offset;
+
+ fb = fileno(fb_in);
+ if(fb < 0) {
+ ALOGE("failed to open framebuffer\n");
+ return;
+ }
+ fb_in = fdopen(fb, "r");
+
+ if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) {
+ ALOGE("failed to get framebuffer info\n");
+ return;
+ }
+ fcntl(fb, F_SETFD, FD_CLOEXEC);
+
+ png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png == NULL) {
+ ALOGE("failed png_create_write_struct\n");
+ fclose(fb_in);
+ return;
+ }
+
+ png_init_io(png, fb_out);
+ info = png_create_info_struct(png);
+ if (info == NULL) {
+ ALOGE("failed png_create_info_struct\n");
+ png_destroy_write_struct(&png, NULL);
+ fclose(fb_in);
+ return;
+ }
+ if (setjmp(png_jmpbuf(png))) {
+ ALOGE("failed png setjmp\n");
+ png_destroy_write_struct(&png, NULL);
+ fclose(fb_in);
+ return;
+ }
+
+ bytespp = vinfo.bits_per_pixel / 8;
+ png_set_IHDR(png, info,
+ vinfo.xres, vinfo.yres, vinfo.bits_per_pixel / 4,
+ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ png_write_info(png, info);
+
+ rowlen=vinfo.xres * bytespp;
+ if (rowlen > sizeof(imgbuf)) {
+ ALOGE("crazy rowlen: %d\n", rowlen);
+ png_destroy_write_struct(&png, NULL);
+ fclose(fb_in);
+ return;
+ }
+
+ offset = vinfo.xoffset * bytespp + vinfo.xres * vinfo.yoffset * bytespp;
+ fseek(fb_in, offset, SEEK_SET);
+
+ for(r=0; r<vinfo.yres; r++) {
+ int len = fread(imgbuf, 1, rowlen, fb_in);
+ if (len <= 0) break;
+ png_write_row(png, (png_bytep)imgbuf);
+ }
+
+ png_write_end(png, info);
+ fclose(fb_in);
+ png_destroy_write_struct(&png, NULL);
+}
+
+void fork_sound(const char* path) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL);
+ }
+}
+
+void usage() {
+ fprintf(stderr,
+ "usage: screenshot [-s soundfile] filename.png\n"
+ " -s: play a sound effect to signal success\n"
+ " -i: autoincrement to avoid overwriting filename.png\n"
+ );
+}
+
+int main(int argc, char**argv) {
+ FILE *png = NULL;
+ FILE *fb_in = NULL;
+ char outfile[PATH_MAX] = "";
+
+ char * soundfile = NULL;
+ int do_increment = 0;
+
+ int c;
+ while ((c = getopt(argc, argv, "s:i")) != -1) {
+ switch (c) {
+ case 's': soundfile = optarg; break;
+ case 'i': do_increment = 1; break;
+ case '?':
+ case 'h':
+ usage(); exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage(); exit(1);
+ }
+
+ strlcpy(outfile, argv[0], PATH_MAX);
+ if (do_increment) {
+ struct stat st;
+ char base[PATH_MAX] = "";
+ int i = 0;
+ while (stat(outfile, &st) == 0) {
+ if (!base[0]) {
+ char *p = strrchr(outfile, '.');
+ if (p) *p = '\0';
+ strcpy(base, outfile);
+ }
+ snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i);
+ }
+ }
+
+ fb_in = fopen("/dev/graphics/fb0", "r");
+ if (!fb_in) {
+ fprintf(stderr, "error: could not read framebuffer\n");
+ exit(1);
+ }
+
+ /* switch to non-root user and group */
+ gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
+ setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+ setuid(AID_SHELL);
+
+ png = fopen(outfile, "w");
+ if (!png) {
+ fprintf(stderr, "error: writing file %s: %s\n",
+ outfile, strerror(errno));
+ exit(1);
+ }
+
+ take_screenshot(fb_in, png);
+
+ if (soundfile) {
+ fork_sound(soundfile);
+ }
+
+ exit(0);
+}
--- /dev/null
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ service.cpp
+
+LOCAL_SHARED_LIBRARIES := libutils libbinder
+
+ifeq ($(TARGET_OS),linux)
+ LOCAL_CFLAGS += -DXP_UNIX
+ #LOCAL_SHARED_LIBRARIES += librt
+endif
+
+LOCAL_MODULE:= service
+
+include $(BUILD_EXECUTABLE)
--- /dev/null
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
--- /dev/null
+/*
+ * Command line access to services.
+ *
+ */
+
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <utils/TextOutput.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+using namespace android;
+
+void writeString16(Parcel& parcel, const char* string)
+{
+ if (string != NULL)
+ {
+ parcel.writeString16(String16(string));
+ }
+ else
+ {
+ parcel.writeInt32(-1);
+ }
+}
+
+// get the name of the generic interface we hold a reference to
+static String16 get_interface_name(sp<IBinder> service)
+{
+ if (service != NULL) {
+ Parcel data, reply;
+ status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply);
+ if (err == NO_ERROR) {
+ return reply.readString16();
+ }
+ }
+ return String16();
+}
+
+static String8 good_old_string(const String16& src)
+{
+ String8 name8;
+ char ch8[2];
+ ch8[1] = 0;
+ for (unsigned j = 0; j < src.size(); j++) {
+ char16_t ch = src[j];
+ if (ch < 128) ch8[0] = (char)ch;
+ name8.append(ch8);
+ }
+ return name8;
+}
+
+int main(int argc, char* const argv[])
+{
+ sp<IServiceManager> sm = defaultServiceManager();
+ fflush(stdout);
+ if (sm == NULL) {
+ aerr << "service: Unable to get default service manager!" << endl;
+ return 20;
+ }
+
+ bool wantsUsage = false;
+ int result = 0;
+
+ while (1) {
+ int ic = getopt(argc, argv, "h?");
+ if (ic < 0)
+ break;
+
+ switch (ic) {
+ case 'h':
+ case '?':
+ wantsUsage = true;
+ break;
+ default:
+ aerr << "service: Unknown option -" << ic << endl;
+ wantsUsage = true;
+ result = 10;
+ break;
+ }
+ }
+
+ if (optind >= argc) {
+ wantsUsage = true;
+ } else if (!wantsUsage) {
+ if (strcmp(argv[optind], "check") == 0) {
+ optind++;
+ if (optind < argc) {
+ sp<IBinder> service = sm->checkService(String16(argv[optind]));
+ aout << "Service " << argv[optind] <<
+ (service == NULL ? ": not found" : ": found") << endl;
+ } else {
+ aerr << "service: No service specified for check" << endl;
+ wantsUsage = true;
+ result = 10;
+ }
+ }
+ else if (strcmp(argv[optind], "list") == 0) {
+ Vector<String16> services = sm->listServices();
+ aout << "Found " << services.size() << " services:" << endl;
+ for (unsigned i = 0; i < services.size(); i++) {
+ String16 name = services[i];
+ sp<IBinder> service = sm->checkService(name);
+ aout << i
+ << "\t" << good_old_string(name)
+ << ": [" << good_old_string(get_interface_name(service)) << "]"
+ << endl;
+ }
+ } else if (strcmp(argv[optind], "call") == 0) {
+ optind++;
+ if (optind+1 < argc) {
+ int serviceArg = optind;
+ sp<IBinder> service = sm->checkService(String16(argv[optind++]));
+ String16 ifName = get_interface_name(service);
+ int32_t code = atoi(argv[optind++]);
+ if (service != NULL && ifName.size() > 0) {
+ Parcel data, reply;
+
+ // the interface name is first
+ data.writeInterfaceToken(ifName);
+
+ // then the rest of the call arguments
+ while (optind < argc) {
+ if (strcmp(argv[optind], "i32") == 0) {
+ optind++;
+ if (optind >= argc) {
+ aerr << "service: no integer supplied for 'i32'" << endl;
+ wantsUsage = true;
+ result = 10;
+ break;
+ }
+ data.writeInt32(atoi(argv[optind++]));
+ } else if (strcmp(argv[optind], "s16") == 0) {
+ optind++;
+ if (optind >= argc) {
+ aerr << "service: no string supplied for 's16'" << endl;
+ wantsUsage = true;
+ result = 10;
+ break;
+ }
+ data.writeString16(String16(argv[optind++]));
+ } else if (strcmp(argv[optind], "null") == 0) {
+ optind++;
+ data.writeStrongBinder(NULL);
+ } else if (strcmp(argv[optind], "intent") == 0) {
+
+ char* action = NULL;
+ char* dataArg = NULL;
+ char* type = NULL;
+ int launchFlags = 0;
+ char* component = NULL;
+ int categoryCount = 0;
+ char* categories[16];
+
+ char* context1 = NULL;
+
+ optind++;
+
+ while (optind < argc)
+ {
+ char* key = strtok_r(argv[optind], "=", &context1);
+ char* value = strtok_r(NULL, "=", &context1);
+
+ // we have reached the end of the XXX=XXX args.
+ if (key == NULL) break;
+
+ if (strcmp(key, "action") == 0)
+ {
+ action = value;
+ }
+ else if (strcmp(key, "data") == 0)
+ {
+ dataArg = value;
+ }
+ else if (strcmp(key, "type") == 0)
+ {
+ type = value;
+ }
+ else if (strcmp(key, "launchFlags") == 0)
+ {
+ launchFlags = atoi(value);
+ }
+ else if (strcmp(key, "component") == 0)
+ {
+ component = value;
+ }
+ else if (strcmp(key, "categories") == 0)
+ {
+ char* context2 = NULL;
+ int categoryCount = 0;
+ categories[categoryCount] = strtok_r(value, ",", &context2);
+
+ while (categories[categoryCount] != NULL)
+ {
+ categoryCount++;
+ categories[categoryCount] = strtok_r(NULL, ",", &context2);
+ }
+ }
+
+ optind++;
+ }
+
+ writeString16(data, action);
+ writeString16(data, dataArg);
+ writeString16(data, type);
+ data.writeInt32(launchFlags);
+ writeString16(data, component);
+
+ if (categoryCount > 0)
+ {
+ data.writeInt32(categoryCount);
+ for (int i = 0 ; i < categoryCount ; i++)
+ {
+ writeString16(data, categories[i]);
+ }
+ }
+ else
+ {
+ data.writeInt32(0);
+ }
+
+ // for now just set the extra field to be null.
+ data.writeInt32(-1);
+ } else {
+ aerr << "service: unknown option " << argv[optind] << endl;
+ wantsUsage = true;
+ result = 10;
+ break;
+ }
+ }
+
+ service->transact(code, data, &reply);
+ aout << "Result: " << reply << endl;
+ } else {
+ aerr << "service: Service " << argv[serviceArg]
+ << " does not exist" << endl;
+ result = 10;
+ }
+ } else {
+ if (optind < argc) {
+ aerr << "service: No service specified for call" << endl;
+ } else {
+ aerr << "service: No code specified for call" << endl;
+ }
+ wantsUsage = true;
+ result = 10;
+ }
+ } else {
+ aerr << "service: Unknown command " << argv[optind] << endl;
+ wantsUsage = true;
+ result = 10;
+ }
+ }
+
+ if (wantsUsage) {
+ aout << "Usage: service [-h|-?]\n"
+ " service list\n"
+ " service check SERVICE\n"
+ " service call SERVICE CODE [i32 INT | s16 STR] ...\n"
+ "Options:\n"
+ " i32: Write the integer INT into the send parcel.\n"
+ " s16: Write the UTF-16 string STR into the send parcel.\n";
+// " intent: Write and Intent int the send parcel. ARGS can be\n"
+// " action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n";
+ return result;
+ }
+
+ return result;
+}
+
--- /dev/null
+LOCAL_PATH:= $(call my-dir)
+
+#include $(CLEAR_VARS)
+#LOCAL_SRC_FILES := bctest.c binder.c
+#LOCAL_MODULE := bctest
+#include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SRC_FILES := service_manager.c binder.c
+LOCAL_MODULE := servicemanager
+include $(BUILD_EXECUTABLE)
--- /dev/null
+/* Copyright 2008 The Android Open Source Project
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "binder.h"
+
+void *svcmgr_lookup(struct binder_state *bs, void *target, const char *name)
+{
+ void *ptr;
+ unsigned iodata[512/4];
+ struct binder_io msg, reply;
+
+ bio_init(&msg, iodata, sizeof(iodata), 4);
+ bio_put_uint32(&msg, 0); // strict mode header
+ bio_put_string16_x(&msg, SVC_MGR_NAME);
+ bio_put_string16_x(&msg, name);
+
+ if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
+ return 0;
+
+ ptr = bio_get_ref(&reply);
+
+ if (ptr)
+ binder_acquire(bs, ptr);
+
+ binder_done(bs, &msg, &reply);
+
+ return ptr;
+}
+
+int svcmgr_publish(struct binder_state *bs, void *target, const char *name, void *ptr)
+{
+ unsigned status;
+ unsigned iodata[512/4];
+ struct binder_io msg, reply;
+
+ bio_init(&msg, iodata, sizeof(iodata), 4);
+ bio_put_uint32(&msg, 0); // strict mode header
+ bio_put_string16_x(&msg, SVC_MGR_NAME);
+ bio_put_string16_x(&msg, name);
+ bio_put_obj(&msg, ptr);
+
+ if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
+ return -1;
+
+ status = bio_get_uint32(&reply);
+
+ binder_done(bs, &msg, &reply);
+
+ return status;
+}
+
+unsigned token;
+
+int main(int argc, char **argv)
+{
+ int fd;
+ struct binder_state *bs;
+ void *svcmgr = BINDER_SERVICE_MANAGER;
+
+ bs = binder_open(128*1024);
+
+ argc--;
+ argv++;
+ while (argc > 0) {
+ if (!strcmp(argv[0],"alt")) {
+ void *ptr = svcmgr_lookup(bs, svcmgr, "alt_svc_mgr");
+ if (!ptr) {
+ fprintf(stderr,"cannot find alt_svc_mgr\n");
+ return -1;
+ }
+ svcmgr = ptr;
+ fprintf(stderr,"svcmgr is via %p\n", ptr);
+ } else if (!strcmp(argv[0],"lookup")) {
+ void *ptr;
+ if (argc < 2) {
+ fprintf(stderr,"argument required\n");
+ return -1;
+ }
+ ptr = svcmgr_lookup(bs, svcmgr, argv[1]);
+ fprintf(stderr,"lookup(%s) = %p\n", argv[1], ptr);
+ argc--;
+ argv++;
+ } else if (!strcmp(argv[0],"publish")) {
+ if (argc < 2) {
+ fprintf(stderr,"argument required\n");
+ return -1;
+ }
+ svcmgr_publish(bs, svcmgr, argv[1], &token);
+ argc--;
+ argv++;
+ } else {
+ fprintf(stderr,"unknown command %s\n", argv[0]);
+ return -1;
+ }
+ argc--;
+ argv++;
+ }
+ return 0;
+}
--- /dev/null
+/* Copyright 2008 The Android Open Source Project
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "binder.h"
+
+#define MAX_BIO_SIZE (1 << 30)
+
+#define TRACE 0
+
+#define LOG_TAG "Binder"
+#include <cutils/log.h>
+
+void bio_init_from_txn(struct binder_io *io, struct binder_txn *txn);
+
+#if TRACE
+void hexdump(void *_data, unsigned len)
+{
+ unsigned char *data = _data;
+ unsigned count;
+
+ for (count = 0; count < len; count++) {
+ if ((count & 15) == 0)
+ fprintf(stderr,"%04x:", count);
+ fprintf(stderr," %02x %c", *data,
+ (*data < 32) || (*data > 126) ? '.' : *data);
+ data++;
+ if ((count & 15) == 15)
+ fprintf(stderr,"\n");
+ }
+ if ((count & 15) != 0)
+ fprintf(stderr,"\n");
+}
+
+void binder_dump_txn(struct binder_txn *txn)
+{
+ struct binder_object *obj;
+ unsigned *offs = txn->offs;
+ unsigned count = txn->offs_size / 4;
+
+ fprintf(stderr," target %p cookie %p code %08x flags %08x\n",
+ txn->target, txn->cookie, txn->code, txn->flags);
+ fprintf(stderr," pid %8d uid %8d data %8d offs %8d\n",
+ txn->sender_pid, txn->sender_euid, txn->data_size, txn->offs_size);
+ hexdump(txn->data, txn->data_size);
+ while (count--) {
+ obj = (void*) (((char*) txn->data) + *offs++);
+ fprintf(stderr," - type %08x flags %08x ptr %p cookie %p\n",
+ obj->type, obj->flags, obj->pointer, obj->cookie);
+ }
+}
+
+#define NAME(n) case n: return #n
+const char *cmd_name(uint32_t cmd)
+{
+ switch(cmd) {
+ NAME(BR_NOOP);
+ NAME(BR_TRANSACTION_COMPLETE);
+ NAME(BR_INCREFS);
+ NAME(BR_ACQUIRE);
+ NAME(BR_RELEASE);
+ NAME(BR_DECREFS);
+ NAME(BR_TRANSACTION);
+ NAME(BR_REPLY);
+ NAME(BR_FAILED_REPLY);
+ NAME(BR_DEAD_REPLY);
+ NAME(BR_DEAD_BINDER);
+ default: return "???";
+ }
+}
+#else
+#define hexdump(a,b) do{} while (0)
+#define binder_dump_txn(txn) do{} while (0)
+#endif
+
+#define BIO_F_SHARED 0x01 /* needs to be buffer freed */
+#define BIO_F_OVERFLOW 0x02 /* ran out of space */
+#define BIO_F_IOERROR 0x04
+#define BIO_F_MALLOCED 0x08 /* needs to be free()'d */
+
+struct binder_state
+{
+ int fd;
+ void *mapped;
+ unsigned mapsize;
+};
+
+struct binder_state *binder_open(unsigned mapsize)
+{
+ struct binder_state *bs;
+
+ bs = malloc(sizeof(*bs));
+ if (!bs) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ bs->fd = open("/dev/binder", O_RDWR);
+ if (bs->fd < 0) {
+ fprintf(stderr,"binder: cannot open device (%s)\n",
+ strerror(errno));
+ goto fail_open;
+ }
+
+ bs->mapsize = mapsize;
+ bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
+ if (bs->mapped == MAP_FAILED) {
+ fprintf(stderr,"binder: cannot map device (%s)\n",
+ strerror(errno));
+ goto fail_map;
+ }
+
+ /* TODO: check version */
+
+ return bs;
+
+fail_map:
+ close(bs->fd);
+fail_open:
+ free(bs);
+ return 0;
+}
+
+void binder_close(struct binder_state *bs)
+{
+ munmap(bs->mapped, bs->mapsize);
+ close(bs->fd);
+ free(bs);
+}
+
+int binder_become_context_manager(struct binder_state *bs)
+{
+ return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
+}
+
+int binder_write(struct binder_state *bs, void *data, unsigned len)
+{
+ struct binder_write_read bwr;
+ int res;
+ bwr.write_size = len;
+ bwr.write_consumed = 0;
+ bwr.write_buffer = (unsigned) data;
+ bwr.read_size = 0;
+ bwr.read_consumed = 0;
+ bwr.read_buffer = 0;
+ res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
+ if (res < 0) {
+ fprintf(stderr,"binder_write: ioctl failed (%s)\n",
+ strerror(errno));
+ }
+ return res;
+}
+
+void binder_send_reply(struct binder_state *bs,
+ struct binder_io *reply,
+ void *buffer_to_free,
+ int status)
+{
+ struct {
+ uint32_t cmd_free;
+ void *buffer;
+ uint32_t cmd_reply;
+ struct binder_txn txn;
+ } __attribute__((packed)) data;
+
+ data.cmd_free = BC_FREE_BUFFER;
+ data.buffer = buffer_to_free;
+ data.cmd_reply = BC_REPLY;
+ data.txn.target = 0;
+ data.txn.cookie = 0;
+ data.txn.code = 0;
+ if (status) {
+ data.txn.flags = TF_STATUS_CODE;
+ data.txn.data_size = sizeof(int);
+ data.txn.offs_size = 0;
+ data.txn.data = &status;
+ data.txn.offs = 0;
+ } else {
+ data.txn.flags = 0;
+ data.txn.data_size = reply->data - reply->data0;
+ data.txn.offs_size = ((char*) reply->offs) - ((char*) reply->offs0);
+ data.txn.data = reply->data0;
+ data.txn.offs = reply->offs0;
+ }
+ binder_write(bs, &data, sizeof(data));
+}
+
+int binder_parse(struct binder_state *bs, struct binder_io *bio,
+ uint32_t *ptr, uint32_t size, binder_handler func)
+{
+ int r = 1;
+ uint32_t *end = ptr + (size / 4);
+
+ while (ptr < end) {
+ uint32_t cmd = *ptr++;
+#if TRACE
+ fprintf(stderr,"%s:\n", cmd_name(cmd));
+#endif
+ switch(cmd) {
+ case BR_NOOP:
+ break;
+ case BR_TRANSACTION_COMPLETE:
+ break;
+ case BR_INCREFS:
+ case BR_ACQUIRE:
+ case BR_RELEASE:
+ case BR_DECREFS:
+#if TRACE
+ fprintf(stderr," %08x %08x\n", ptr[0], ptr[1]);
+#endif
+ ptr += 2;
+ break;
+ case BR_TRANSACTION: {
+ struct binder_txn *txn = (void *) ptr;
+ if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
+ ALOGE("parse: txn too small!\n");
+ return -1;
+ }
+ binder_dump_txn(txn);
+ if (func) {
+ unsigned rdata[256/4];
+ struct binder_io msg;
+ struct binder_io reply;
+ int res;
+
+ bio_init(&reply, rdata, sizeof(rdata), 4);
+ bio_init_from_txn(&msg, txn);
+ res = func(bs, txn, &msg, &reply);
+ binder_send_reply(bs, &reply, txn->data, res);
+ }
+ ptr += sizeof(*txn) / sizeof(uint32_t);
+ break;
+ }
+ case BR_REPLY: {
+ struct binder_txn *txn = (void*) ptr;
+ if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
+ ALOGE("parse: reply too small!\n");
+ return -1;
+ }
+ binder_dump_txn(txn);
+ if (bio) {
+ bio_init_from_txn(bio, txn);
+ bio = 0;
+ } else {
+ /* todo FREE BUFFER */
+ }
+ ptr += (sizeof(*txn) / sizeof(uint32_t));
+ r = 0;
+ break;
+ }
+ case BR_DEAD_BINDER: {
+ struct binder_death *death = (void*) *ptr++;
+ death->func(bs, death->ptr);
+ break;
+ }
+ case BR_FAILED_REPLY:
+ r = -1;
+ break;
+ case BR_DEAD_REPLY:
+ r = -1;
+ break;
+ default:
+ ALOGE("parse: OOPS %d\n", cmd);
+ return -1;
+ }
+ }
+
+ return r;
+}
+
+void binder_acquire(struct binder_state *bs, void *ptr)
+{
+ uint32_t cmd[2];
+ cmd[0] = BC_ACQUIRE;
+ cmd[1] = (uint32_t) ptr;
+ binder_write(bs, cmd, sizeof(cmd));
+}
+
+void binder_release(struct binder_state *bs, void *ptr)
+{
+ uint32_t cmd[2];
+ cmd[0] = BC_RELEASE;
+ cmd[1] = (uint32_t) ptr;
+ binder_write(bs, cmd, sizeof(cmd));
+}
+
+void binder_link_to_death(struct binder_state *bs, void *ptr, struct binder_death *death)
+{
+ uint32_t cmd[3];
+ cmd[0] = BC_REQUEST_DEATH_NOTIFICATION;
+ cmd[1] = (uint32_t) ptr;
+ cmd[2] = (uint32_t) death;
+ binder_write(bs, cmd, sizeof(cmd));
+}
+
+
+int binder_call(struct binder_state *bs,
+ struct binder_io *msg, struct binder_io *reply,
+ void *target, uint32_t code)
+{
+ int res;
+ struct binder_write_read bwr;
+ struct {
+ uint32_t cmd;
+ struct binder_txn txn;
+ } writebuf;
+ unsigned readbuf[32];
+
+ if (msg->flags & BIO_F_OVERFLOW) {
+ fprintf(stderr,"binder: txn buffer overflow\n");
+ goto fail;
+ }
+
+ writebuf.cmd = BC_TRANSACTION;
+ writebuf.txn.target = target;
+ writebuf.txn.code = code;
+ writebuf.txn.flags = 0;
+ writebuf.txn.data_size = msg->data - msg->data0;
+ writebuf.txn.offs_size = ((char*) msg->offs) - ((char*) msg->offs0);
+ writebuf.txn.data = msg->data0;
+ writebuf.txn.offs = msg->offs0;
+
+ bwr.write_size = sizeof(writebuf);
+ bwr.write_consumed = 0;
+ bwr.write_buffer = (unsigned) &writebuf;
+
+ hexdump(msg->data0, msg->data - msg->data0);
+ for (;;) {
+ bwr.read_size = sizeof(readbuf);
+ bwr.read_consumed = 0;
+ bwr.read_buffer = (unsigned) readbuf;
+
+ res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
+
+ if (res < 0) {
+ fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno));
+ goto fail;
+ }
+
+ res = binder_parse(bs, reply, readbuf, bwr.read_consumed, 0);
+ if (res == 0) return 0;
+ if (res < 0) goto fail;
+ }
+
+fail:
+ memset(reply, 0, sizeof(*reply));
+ reply->flags |= BIO_F_IOERROR;
+ return -1;
+}
+
+void binder_loop(struct binder_state *bs, binder_handler func)
+{
+ int res;
+ struct binder_write_read bwr;
+ unsigned readbuf[32];
+
+ bwr.write_size = 0;
+ bwr.write_consumed = 0;
+ bwr.write_buffer = 0;
+
+ readbuf[0] = BC_ENTER_LOOPER;
+ binder_write(bs, readbuf, sizeof(unsigned));
+
+ for (;;) {
+ bwr.read_size = sizeof(readbuf);
+ bwr.read_consumed = 0;
+ bwr.read_buffer = (unsigned) readbuf;
+
+ res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
+
+ if (res < 0) {
+ ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
+ break;
+ }
+
+ res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
+ if (res == 0) {
+ ALOGE("binder_loop: unexpected reply?!\n");
+ break;
+ }
+ if (res < 0) {
+ ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
+ break;
+ }
+ }
+}
+
+void bio_init_from_txn(struct binder_io *bio, struct binder_txn *txn)
+{
+ bio->data = bio->data0 = txn->data;
+ bio->offs = bio->offs0 = txn->offs;
+ bio->data_avail = txn->data_size;
+ bio->offs_avail = txn->offs_size / 4;
+ bio->flags = BIO_F_SHARED;
+}
+
+void bio_init(struct binder_io *bio, void *data,
+ uint32_t maxdata, uint32_t maxoffs)
+{
+ uint32_t n = maxoffs * sizeof(uint32_t);
+
+ if (n > maxdata) {
+ bio->flags = BIO_F_OVERFLOW;
+ bio->data_avail = 0;
+ bio->offs_avail = 0;
+ return;
+ }
+
+ bio->data = bio->data0 = (char *) data + n;
+ bio->offs = bio->offs0 = data;
+ bio->data_avail = maxdata - n;
+ bio->offs_avail = maxoffs;
+ bio->flags = 0;
+}
+
+static void *bio_alloc(struct binder_io *bio, uint32_t size)
+{
+ size = (size + 3) & (~3);
+ if (size > bio->data_avail) {
+ bio->flags |= BIO_F_OVERFLOW;
+ return 0;
+ } else {
+ void *ptr = bio->data;
+ bio->data += size;
+ bio->data_avail -= size;
+ return ptr;
+ }
+}
+
+void binder_done(struct binder_state *bs,
+ struct binder_io *msg,
+ struct binder_io *reply)
+{
+ if (reply->flags & BIO_F_SHARED) {
+ uint32_t cmd[2];
+ cmd[0] = BC_FREE_BUFFER;
+ cmd[1] = (uint32_t) reply->data0;
+ binder_write(bs, cmd, sizeof(cmd));
+ reply->flags = 0;
+ }
+}
+
+static struct binder_object *bio_alloc_obj(struct binder_io *bio)
+{
+ struct binder_object *obj;
+
+ obj = bio_alloc(bio, sizeof(*obj));
+
+ if (obj && bio->offs_avail) {
+ bio->offs_avail--;
+ *bio->offs++ = ((char*) obj) - ((char*) bio->data0);
+ return obj;
+ }
+
+ bio->flags |= BIO_F_OVERFLOW;
+ return 0;
+}
+
+void bio_put_uint32(struct binder_io *bio, uint32_t n)
+{
+ uint32_t *ptr = bio_alloc(bio, sizeof(n));
+ if (ptr)
+ *ptr = n;
+}
+
+void bio_put_obj(struct binder_io *bio, void *ptr)
+{
+ struct binder_object *obj;
+
+ obj = bio_alloc_obj(bio);
+ if (!obj)
+ return;
+
+ obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ obj->type = BINDER_TYPE_BINDER;
+ obj->pointer = ptr;
+ obj->cookie = 0;
+}
+
+void bio_put_ref(struct binder_io *bio, void *ptr)
+{
+ struct binder_object *obj;
+
+ if (ptr)
+ obj = bio_alloc_obj(bio);
+ else
+ obj = bio_alloc(bio, sizeof(*obj));
+
+ if (!obj)
+ return;
+
+ obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ obj->type = BINDER_TYPE_HANDLE;
+ obj->pointer = ptr;
+ obj->cookie = 0;
+}
+
+void bio_put_string16(struct binder_io *bio, const uint16_t *str)
+{
+ uint32_t len;
+ uint16_t *ptr;
+
+ if (!str) {
+ bio_put_uint32(bio, 0xffffffff);
+ return;
+ }
+
+ len = 0;
+ while (str[len]) len++;
+
+ if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) {
+ bio_put_uint32(bio, 0xffffffff);
+ return;
+ }
+
+ bio_put_uint32(bio, len);
+ len = (len + 1) * sizeof(uint16_t);
+ ptr = bio_alloc(bio, len);
+ if (ptr)
+ memcpy(ptr, str, len);
+}
+
+void bio_put_string16_x(struct binder_io *bio, const char *_str)
+{
+ unsigned char *str = (unsigned char*) _str;
+ uint32_t len;
+ uint16_t *ptr;
+
+ if (!str) {
+ bio_put_uint32(bio, 0xffffffff);
+ return;
+ }
+
+ len = strlen(_str);
+
+ if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) {
+ bio_put_uint32(bio, 0xffffffff);
+ return;
+ }
+
+ bio_put_uint32(bio, len);
+ ptr = bio_alloc(bio, (len + 1) * sizeof(uint16_t));
+ if (!ptr)
+ return;
+
+ while (*str)
+ *ptr++ = *str++;
+ *ptr++ = 0;
+}
+
+static void *bio_get(struct binder_io *bio, uint32_t size)
+{
+ size = (size + 3) & (~3);
+
+ if (bio->data_avail < size){
+ bio->data_avail = 0;
+ bio->flags |= BIO_F_OVERFLOW;
+ return 0;
+ } else {
+ void *ptr = bio->data;
+ bio->data += size;
+ bio->data_avail -= size;
+ return ptr;
+ }
+}
+
+uint32_t bio_get_uint32(struct binder_io *bio)
+{
+ uint32_t *ptr = bio_get(bio, sizeof(*ptr));
+ return ptr ? *ptr : 0;
+}
+
+uint16_t *bio_get_string16(struct binder_io *bio, unsigned *sz)
+{
+ unsigned len;
+ len = bio_get_uint32(bio);
+ if (sz)
+ *sz = len;
+ return bio_get(bio, (len + 1) * sizeof(uint16_t));
+}
+
+static struct binder_object *_bio_get_obj(struct binder_io *bio)
+{
+ unsigned n;
+ unsigned off = bio->data - bio->data0;
+
+ /* TODO: be smarter about this? */
+ for (n = 0; n < bio->offs_avail; n++) {
+ if (bio->offs[n] == off)
+ return bio_get(bio, sizeof(struct binder_object));
+ }
+
+ bio->data_avail = 0;
+ bio->flags |= BIO_F_OVERFLOW;
+ return 0;
+}
+
+void *bio_get_ref(struct binder_io *bio)
+{
+ struct binder_object *obj;
+
+ obj = _bio_get_obj(bio);
+ if (!obj)
+ return 0;
+
+ if (obj->type == BINDER_TYPE_HANDLE)
+ return obj->pointer;
+
+ return 0;
+}
--- /dev/null
+/* Copyright 2008 The Android Open Source Project
+ */
+
+#ifndef _BINDER_H_
+#define _BINDER_H_
+
+#include <sys/ioctl.h>
+#include <linux/binder.h>
+
+struct binder_state;
+
+struct binder_object
+{
+ uint32_t type;
+ uint32_t flags;
+ void *pointer;
+ void *cookie;
+};
+
+struct binder_txn
+{
+ void *target;
+ void *cookie;
+ uint32_t code;
+ uint32_t flags;
+
+ uint32_t sender_pid;
+ uint32_t sender_euid;
+
+ uint32_t data_size;
+ uint32_t offs_size;
+ void *data;
+ void *offs;
+};
+
+struct binder_io
+{
+ char *data; /* pointer to read/write from */
+ uint32_t *offs; /* array of offsets */
+ uint32_t data_avail; /* bytes available in data buffer */
+ uint32_t offs_avail; /* entries available in offsets array */
+
+ char *data0; /* start of data buffer */
+ uint32_t *offs0; /* start of offsets buffer */
+ uint32_t flags;
+ uint32_t unused;
+};
+
+struct binder_death {
+ void (*func)(struct binder_state *bs, void *ptr);
+ void *ptr;
+};
+
+/* the one magic object */
+#define BINDER_SERVICE_MANAGER ((void*) 0)
+
+#define SVC_MGR_NAME "android.os.IServiceManager"
+
+enum {
+ SVC_MGR_GET_SERVICE = 1,
+ SVC_MGR_CHECK_SERVICE,
+ SVC_MGR_ADD_SERVICE,
+ SVC_MGR_LIST_SERVICES,
+};
+
+typedef int (*binder_handler)(struct binder_state *bs,
+ struct binder_txn *txn,
+ struct binder_io *msg,
+ struct binder_io *reply);
+
+struct binder_state *binder_open(unsigned mapsize);
+void binder_close(struct binder_state *bs);
+
+/* initiate a blocking binder call
+ * - returns zero on success
+ */
+int binder_call(struct binder_state *bs,
+ struct binder_io *msg, struct binder_io *reply,
+ void *target, uint32_t code);
+
+/* release any state associate with the binder_io
+ * - call once any necessary data has been extracted from the
+ * binder_io after binder_call() returns
+ * - can safely be called even if binder_call() fails
+ */
+void binder_done(struct binder_state *bs,
+ struct binder_io *msg, struct binder_io *reply);
+
+/* manipulate strong references */
+void binder_acquire(struct binder_state *bs, void *ptr);
+void binder_release(struct binder_state *bs, void *ptr);
+
+void binder_link_to_death(struct binder_state *bs, void *ptr, struct binder_death *death);
+
+void binder_loop(struct binder_state *bs, binder_handler func);
+
+int binder_become_context_manager(struct binder_state *bs);
+
+/* allocate a binder_io, providing a stack-allocated working
+ * buffer, size of the working buffer, and how many object
+ * offset entries to reserve from the buffer
+ */
+void bio_init(struct binder_io *bio, void *data,
+ uint32_t maxdata, uint32_t maxobjects);
+
+void bio_destroy(struct binder_io *bio);
+
+void bio_put_obj(struct binder_io *bio, void *ptr);
+void bio_put_ref(struct binder_io *bio, void *ptr);
+void bio_put_uint32(struct binder_io *bio, uint32_t n);
+void bio_put_string16(struct binder_io *bio, const uint16_t *str);
+void bio_put_string16_x(struct binder_io *bio, const char *_str);
+
+uint32_t bio_get_uint32(struct binder_io *bio);
+uint16_t *bio_get_string16(struct binder_io *bio, uint32_t *sz);
+void *bio_get_obj(struct binder_io *bio);
+void *bio_get_ref(struct binder_io *bio);
+
+#endif
--- /dev/null
+/* Copyright 2008 The Android Open Source Project
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "binder.h"
+
+#if 0
+#define ALOGI(x...) fprintf(stderr, "svcmgr: " x)
+#define ALOGE(x...) fprintf(stderr, "svcmgr: " x)
+#else
+#define LOG_TAG "ServiceManager"
+#include <cutils/log.h>
+#endif
+
+/* TODO:
+ * These should come from a config file or perhaps be
+ * based on some namespace rules of some sort (media
+ * uid can register media.*, etc)
+ */
+static struct {
+ unsigned uid;
+ const char *name;
+} allowed[] = {
+ { AID_MEDIA, "media.audio_flinger" },
+ { AID_MEDIA, "media.log" },
+ { AID_MEDIA, "media.player" },
+ { AID_MEDIA, "media.camera" },
+ { AID_MEDIA, "media.audio_policy" },
+ { AID_DRM, "drm.drmManager" },
+ { AID_NFC, "nfc" },
+ { AID_BLUETOOTH, "bluetooth" },
+ { AID_RADIO, "radio.phone" },
+ { AID_RADIO, "radio.sms" },
+ { AID_RADIO, "radio.phonesubinfo" },
+ { AID_RADIO, "radio.simphonebook" },
+/* TODO: remove after phone services are updated: */
+ { AID_RADIO, "phone" },
+ { AID_RADIO, "sip" },
+ { AID_RADIO, "isms" },
+ { AID_RADIO, "iphonesubinfo" },
+ { AID_RADIO, "simphonebook" },
+ { AID_MEDIA, "common_time.clock" },
+ { AID_MEDIA, "common_time.config" },
+ { AID_KEYSTORE, "android.security.keystore" },
+};
+
+void *svcmgr_handle;
+
+const char *str8(uint16_t *x)
+{
+ static char buf[128];
+ unsigned max = 127;
+ char *p = buf;
+
+ if (x) {
+ while (*x && max--) {
+ *p++ = *x++;
+ }
+ }
+ *p++ = 0;
+ return buf;
+}
+
+int str16eq(uint16_t *a, const char *b)
+{
+ while (*a && *b)
+ if (*a++ != *b++) return 0;
+ if (*a || *b)
+ return 0;
+ return 1;
+}
+
+int svc_can_register(unsigned uid, uint16_t *name)
+{
+ unsigned n;
+
+ if ((uid == 0) || (uid == AID_SYSTEM))
+ return 1;
+
+ for (n = 0; n < sizeof(allowed) / sizeof(allowed[0]); n++)
+ if ((uid == allowed[n].uid) && str16eq(name, allowed[n].name))
+ return 1;
+
+ return 0;
+}
+
+struct svcinfo
+{
+ struct svcinfo *next;
+ void *ptr;
+ struct binder_death death;
+ int allow_isolated;
+ unsigned len;
+ uint16_t name[0];
+};
+
+struct svcinfo *svclist = 0;
+
+struct svcinfo *find_svc(uint16_t *s16, unsigned len)
+{
+ struct svcinfo *si;
+
+ for (si = svclist; si; si = si->next) {
+ if ((len == si->len) &&
+ !memcmp(s16, si->name, len * sizeof(uint16_t))) {
+ return si;
+ }
+ }
+ return 0;
+}
+
+void svcinfo_death(struct binder_state *bs, void *ptr)
+{
+ struct svcinfo *si = ptr;
+ ALOGI("service '%s' died\n", str8(si->name));
+ if (si->ptr) {
+ binder_release(bs, si->ptr);
+ si->ptr = 0;
+ }
+}
+
+uint16_t svcmgr_id[] = {
+ 'a','n','d','r','o','i','d','.','o','s','.',
+ 'I','S','e','r','v','i','c','e','M','a','n','a','g','e','r'
+};
+
+
+void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len, unsigned uid)
+{
+ struct svcinfo *si;
+ si = find_svc(s, len);
+
+// ALOGI("check_service('%s') ptr = %p\n", str8(s), si ? si->ptr : 0);
+ if (si && si->ptr) {
+ if (!si->allow_isolated) {
+ // If this service doesn't allow access from isolated processes,
+ // then check the uid to see if it is isolated.
+ unsigned appid = uid % AID_USER;
+ if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
+ return 0;
+ }
+ }
+ return si->ptr;
+ } else {
+ return 0;
+ }
+}
+
+int do_add_service(struct binder_state *bs,
+ uint16_t *s, unsigned len,
+ void *ptr, unsigned uid, int allow_isolated)
+{
+ struct svcinfo *si;
+ //ALOGI("add_service('%s',%p,%s) uid=%d\n", str8(s), ptr,
+ // allow_isolated ? "allow_isolated" : "!allow_isolated", uid);
+
+ if (!ptr || (len == 0) || (len > 127))
+ return -1;
+
+ if (!svc_can_register(uid, s)) {
+ ALOGE("add_service('%s',%p) uid=%d - PERMISSION DENIED\n",
+ str8(s), ptr, uid);
+ return -1;
+ }
+
+ si = find_svc(s, len);
+ if (si) {
+ if (si->ptr) {
+ ALOGE("add_service('%s',%p) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
+ str8(s), ptr, uid);
+ svcinfo_death(bs, si);
+ }
+ si->ptr = ptr;
+ } else {
+ si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
+ if (!si) {
+ ALOGE("add_service('%s',%p) uid=%d - OUT OF MEMORY\n",
+ str8(s), ptr, uid);
+ return -1;
+ }
+ si->ptr = ptr;
+ si->len = len;
+ memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
+ si->name[len] = '\0';
+ si->death.func = svcinfo_death;
+ si->death.ptr = si;
+ si->allow_isolated = allow_isolated;
+ si->next = svclist;
+ svclist = si;
+ }
+
+ binder_acquire(bs, ptr);
+ binder_link_to_death(bs, ptr, &si->death);
+ return 0;
+}
+
+int svcmgr_handler(struct binder_state *bs,
+ struct binder_txn *txn,
+ struct binder_io *msg,
+ struct binder_io *reply)
+{
+ struct svcinfo *si;
+ uint16_t *s;
+ unsigned len;
+ void *ptr;
+ uint32_t strict_policy;
+ int allow_isolated;
+
+// ALOGI("target=%p code=%d pid=%d uid=%d\n",
+// txn->target, txn->code, txn->sender_pid, txn->sender_euid);
+
+ if (txn->target != svcmgr_handle)
+ return -1;
+
+ // Equivalent to Parcel::enforceInterface(), reading the RPC
+ // header with the strict mode policy mask and the interface name.
+ // Note that we ignore the strict_policy and don't propagate it
+ // further (since we do no outbound RPCs anyway).
+ strict_policy = bio_get_uint32(msg);
+ s = bio_get_string16(msg, &len);
+ if ((len != (sizeof(svcmgr_id) / 2)) ||
+ memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
+ fprintf(stderr,"invalid id %s\n", str8(s));
+ return -1;
+ }
+
+ switch(txn->code) {
+ case SVC_MGR_GET_SERVICE:
+ case SVC_MGR_CHECK_SERVICE:
+ s = bio_get_string16(msg, &len);
+ ptr = do_find_service(bs, s, len, txn->sender_euid);
+ if (!ptr)
+ break;
+ bio_put_ref(reply, ptr);
+ return 0;
+
+ case SVC_MGR_ADD_SERVICE:
+ s = bio_get_string16(msg, &len);
+ ptr = bio_get_ref(msg);
+ allow_isolated = bio_get_uint32(msg) ? 1 : 0;
+ if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated))
+ return -1;
+ break;
+
+ case SVC_MGR_LIST_SERVICES: {
+ unsigned n = bio_get_uint32(msg);
+
+ si = svclist;
+ while ((n-- > 0) && si)
+ si = si->next;
+ if (si) {
+ bio_put_string16(reply, si->name);
+ return 0;
+ }
+ return -1;
+ }
+ default:
+ ALOGE("unknown code %d\n", txn->code);
+ return -1;
+ }
+
+ bio_put_uint32(reply, 0);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct binder_state *bs;
+ void *svcmgr = BINDER_SERVICE_MANAGER;
+
+ bs = binder_open(128*1024);
+
+ if (binder_become_context_manager(bs)) {
+ ALOGE("cannot become context manager (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ svcmgr_handle = svcmgr;
+ binder_loop(bs, svcmgr_handler);
+ return 0;
+}
ACONFIGURATION_DENSITY_HIGH = 240,
ACONFIGURATION_DENSITY_XHIGH = 320,
ACONFIGURATION_DENSITY_XXHIGH = 480,
+ ACONFIGURATION_DENSITY_XXXHIGH = 640,
ACONFIGURATION_DENSITY_NONE = 0xffff,
ACONFIGURATION_KEYBOARD_ANY = 0x0000,
AKEYCODE_RO = 217,
AKEYCODE_KANA = 218,
AKEYCODE_ASSIST = 219,
+ AKEYCODE_BRIGHTNESS_DOWN = 220,
+ AKEYCODE_BRIGHTNESS_UP = 221,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
--- /dev/null
+/*
+ * Copyright (C) 2013 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 ANDROID_APP_OPS_MANAGER_H
+#define ANDROID_APP_OPS_MANAGER_H
+
+#include <binder/IAppOpsService.h>
+
+#include <utils/threads.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class AppOpsManager
+{
+public:
+ enum {
+ MODE_ALLOWED = 0,
+ MODE_IGNORED = 1,
+ MODE_ERRORED = 2
+ };
+
+ enum {
+ OP_NONE = -1,
+ OP_COARSE_LOCATION = 0,
+ OP_FINE_LOCATION = 1,
+ OP_GPS = 2,
+ OP_VIBRATE = 3,
+ OP_READ_CONTACTS = 4,
+ OP_WRITE_CONTACTS = 5,
+ OP_READ_CALL_LOG = 6,
+ OP_WRITE_CALL_LOG = 7,
+ OP_READ_CALENDAR = 8,
+ OP_WRITE_CALENDAR = 9,
+ OP_WIFI_SCAN = 10,
+ OP_POST_NOTIFICATION = 11,
+ OP_NEIGHBORING_CELLS = 12,
+ OP_CALL_PHONE = 13,
+ OP_READ_SMS = 14,
+ OP_WRITE_SMS = 15,
+ OP_RECEIVE_SMS = 16,
+ OP_RECEIVE_EMERGECY_SMS = 17,
+ OP_RECEIVE_MMS = 18,
+ OP_RECEIVE_WAP_PUSH = 19,
+ OP_SEND_SMS = 20,
+ OP_READ_ICC_SMS = 21,
+ OP_WRITE_ICC_SMS = 22,
+ OP_WRITE_SETTINGS = 23,
+ OP_SYSTEM_ALERT_WINDOW = 24,
+ OP_ACCESS_NOTIFICATIONS = 25,
+ OP_CAMERA = 26,
+ OP_RECORD_AUDIO = 27,
+ OP_PLAY_AUDIO = 28
+ };
+
+ AppOpsManager();
+
+ int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
+ int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
+ int32_t startOp(int32_t op, int32_t uid, const String16& callingPackage);
+ void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
+ void startWatchingMode(int32_t op, const String16& packageName,
+ const sp<IAppOpsCallback>& callback);
+ void stopWatchingMode(const sp<IAppOpsCallback>& callback);
+
+private:
+ Mutex mLock;
+ sp<IAppOpsService> mService;
+
+ sp<IAppOpsService> getService();
+};
+
+
+}; // namespace android
+// ---------------------------------------------------------------------------
+#endif // ANDROID_APP_OPS_MANAGER_H
--- /dev/null
+/*
+ * Copyright (C) 2013 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 ANDROID_IAPP_OPS_CALLBACK_H
+#define ANDROID_IAPP_OPS_CALLBACK_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IAppOpsCallback : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AppOpsCallback);
+
+ virtual void opChanged(int32_t op, const String16& packageName) = 0;
+
+ enum {
+ OP_CHANGED_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnAppOpsCallback : public BnInterface<IAppOpsCallback>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAPP_OPS_CALLBACK_H
+
--- /dev/null
+/*
+ * Copyright (C) 2013 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 ANDROID_IAPP_OPS_SERVICE_H
+#define ANDROID_IAPP_OPS_SERVICE_H
+
+#include <binder/IAppOpsCallback.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IAppOpsService : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AppOpsService);
+
+ virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual int32_t startOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual void finishOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+ virtual void startWatchingMode(int32_t op, const String16& packageName,
+ const sp<IAppOpsCallback>& callback) = 0;
+ virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
+
+ enum {
+ CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1,
+ START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2,
+ FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
+ START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
+ STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnAppOpsService : public BnInterface<IAppOpsService>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAPP_OPS_SERVICE_H
+
--- /dev/null
+/*
+ *
+ * Copyright (C) 2010 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 __LIBDISKUSAGE_DIRSIZE_H
+#define __LIBDISKUSAGE_DIRSIZE_H
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+int64_t stat_size(struct stat *s);
+int64_t calculate_dir_size(int dfd);
+
+__END_DECLS
+
+#endif /* __LIBDISKUSAGE_DIRSIZE_H */
status_t releaseBuffer(const BufferItem &item,
const sp<Fence>& releaseFence = Fence::NO_FENCE);
- sp<ISurfaceTexture> getProducerInterface() const { return getBufferQueue(); }
+ sp<IGraphicBufferProducer> getProducerInterface() const { return getBufferQueue(); }
};
#include <EGL/eglext.h>
#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
namespace android {
// ----------------------------------------------------------------------------
-class BufferQueue : public BnSurfaceTexture {
+class BufferQueue : public BnGraphicBufferProducer {
public:
enum { MIN_UNDEQUEUED_BUFFERS = 2 };
enum { NUM_BUFFER_SLOTS = 32 };
virtual status_t setSynchronousMode(bool enabled);
// connect attempts to connect a producer client API to the BufferQueue.
- // This must be called before any other ISurfaceTexture methods are called
+ // This must be called before any other IGraphicBufferProducer methods are called
// except for getAllocator.
//
// This method will fail if the connect was previously called on the
// disconnect attempts to disconnect a producer client API from the
// BufferQueue. Calling this method will cause any subsequent calls to other
- // ISurfaceTexture methods to fail except for getAllocator and connect.
+ // IGraphicBufferProducer methods to fail except for getAllocator and connect.
// Successfully calling connect after this will allow the other methods to
// succeed again.
//
mBuf(INVALID_BUFFER_SLOT) {
mCrop.makeInvalid();
}
- // mGraphicBuffer points to the buffer allocated for this slot or is NULL
- // if no buffer has been allocated.
+ // mGraphicBuffer points to the buffer allocated for this slot, or is NULL
+ // if the buffer in this slot has been acquired in the past (see
+ // BufferSlot.mAcquireCalled).
sp<GraphicBuffer> mGraphicBuffer;
// mCrop is the current crop rectangle for this buffer slot.
// producer is connected to the BufferQueue.
status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
- // isSynchronousMode returns whether the SurfaceTexture is currently in
+ // isSynchronousMode returns whether the BufferQueue is currently in
// synchronous mode.
bool isSynchronousMode() const;
Fifo mQueue;
// mAbandoned indicates that the BufferQueue will no longer be used to
- // consume images buffers pushed to it using the ISurfaceTexture interface.
+ // consume images buffers pushed to it using the IGraphicBufferProducer interface.
// It is initialized to false, and set to true in the abandon method. A
// BufferQueue that has been abandoned will return the NO_INIT error from
- // all ISurfaceTexture methods capable of returning an error.
+ // all IGraphicBufferProducer methods capable of returning an error.
bool mAbandoned;
// mName is a string used to identify the BufferQueue in log messages.
// abandon frees all the buffers and puts the ConsumerBase into the
// 'abandoned' state. Once put in this state the ConsumerBase can never
// leave it. When in the 'abandoned' state, all methods of the
- // ISurfaceTexture interface will fail with the NO_INIT error.
+ // IGraphicBufferProducer interface will fail with the NO_INIT error.
//
// Note that while calling this method causes all the buffers to be freed
// from the perspective of the the ConsumerBase, if there are additional
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
- void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
+ void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
private:
ConsumerBase(const ConsumerBase&);
Slot mSlots[BufferQueue::NUM_BUFFER_SLOTS];
// mAbandoned indicates that the BufferQueue will no longer be used to
- // consume images buffers pushed to it using the ISurfaceTexture
+ // consume images buffers pushed to it using the IGraphicBufferProducer
// interface. It is initialized to false, and set to true in the abandon
// method. A BufferQueue that has been abandoned will return the NO_INIT
// error from all IConsumerBase methods capable of returning an error.
// mFrameAvailableListener is the listener object that will be called when a
// new frame becomes available. If it is not NULL it will be called from
// queueBuffer.
- sp<FrameAvailableListener> mFrameAvailableListener;
+ wp<FrameAvailableListener> mFrameAvailableListener;
// The ConsumerBase has-a BufferQueue and is responsible for creating this object
// if none is supplied
// lockNextBuffer.
status_t unlockBuffer(const LockedBuffer &nativeBuffer);
- sp<ISurfaceTexture> getProducerInterface() const { return getBufferQueue(); }
+ sp<IGraphicBufferProducer> getProducerInterface() const { return getBufferQueue(); }
private:
// Maximum number of buffers that can be locked at a time
// The DummyConsumer does not keep a reference to BufferQueue
-// unlike SurfaceTexture. This prevents a circular reference from
+// unlike GLConsumer. This prevents a circular reference from
// forming without having to use a ProxyConsumerListener
class DummyConsumer : public BufferQueue::ConsumerListener {
public:
protected:
// Implementation of the BufferQueue::ConsumerListener interface. These
- // calls are used to notify the SurfaceTexture of asynchronous events in the
+ // calls are used to notify the GLConsumer of asynchronous events in the
// BufferQueue.
virtual void onFrameAvailable();
virtual void onBuffersReleased();
* limitations under the License.
*/
-#ifndef ANDROID_GUI_SURFACETEXTURE_H
-#define ANDROID_GUI_SURFACETEXTURE_H
+#ifndef ANDROID_GUI_CONSUMER_H
+#define ANDROID_GUI_CONSUMER_H
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <utils/threads.h>
#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
+#define ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID \
+ "mFrameAvailableListener"
namespace android {
// ----------------------------------------------------------------------------
class String8;
-class SurfaceTexture : public ConsumerBase {
+/*
+ * GLConsumer consumes buffers of graphics data from a BufferQueue,
+ * and makes them available to OpenGL as a texture.
+ *
+ * A typical usage pattern is to set up the GLConsumer with the
+ * desired options, and call updateTexImage() when a new frame is desired.
+ * If a new frame is available, the texture will be updated. If not,
+ * the previous contents are retained.
+ *
+ * By default, the texture is attached to the GL_TEXTURE_EXTERNAL_OES
+ * texture target, in the EGL context of the first thread that calls
+ * updateTexImage().
+ *
+ * This class was previously called SurfaceTexture.
+ */
+class GLConsumer : public ConsumerBase {
public:
typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
- // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
+ // GLConsumer constructs a new GLConsumer object. tex indicates the
// name of the OpenGL ES texture to which images are to be streamed.
// allowSynchronousMode specifies whether or not synchronous mode can be
// enabled. texTarget specifies the OpenGL ES texture target to which the
// if behavior for queue/dequeue/connect etc needs to be customized.
// Otherwise a default BufferQueue will be created and used.
//
- // For legacy reasons, the SurfaceTexture is created in a state where it is
+ // For legacy reasons, the GLConsumer is created in a state where it is
// considered attached to an OpenGL ES context for the purposes of the
// attachToContext and detachFromContext methods. However, despite being
// considered "attached" to a context, the specific OpenGL ES context
// point, all calls to updateTexImage must be made with the same OpenGL ES
// context current.
//
- // A SurfaceTexture may be detached from one OpenGL ES context and then
+ // A GLConsumer may be detached from one OpenGL ES context and then
// attached to a different context using the detachFromContext and
// attachToContext methods, respectively. The intention of these methods is
- // purely to allow a SurfaceTexture to be transferred from one consumer
+ // purely to allow a GLConsumer to be transferred from one consumer
// context to another. If such a transfer is not needed there is no
// requirement that either of these methods be called.
- SurfaceTexture(GLuint tex, bool allowSynchronousMode = true,
+ GLConsumer(GLuint tex, bool allowSynchronousMode = true,
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
const sp<BufferQueue> &bufferQueue = 0);
- // updateTexImage sets the image contents of the target texture to that of
- // the most recently queued buffer.
+ // updateTexImage acquires the most recently queued buffer, and sets the
+ // image contents of the target texture to it.
//
// This call may only be made while the OpenGL ES context to which the
// target texture belongs is bound to the calling thread.
//
- // After calling this method the doGLFenceWait method must be called
- // before issuing OpenGL ES commands that access the texture contents.
+ // This calls doGLFenceWait to ensure proper synchronization.
status_t updateTexImage();
// setReleaseFence stores a fence file descriptor that will signal when the
// current buffer is no longer being read. This fence will be returned to
// the producer when the current buffer is released by updateTexImage().
// Multiple fences can be set for a given buffer; they will be merged into
- // a single union fence. The SurfaceTexture will close the file descriptor
+ // a single union fence. The GLConsumer will close the file descriptor
// when finished with it.
void setReleaseFence(int fenceFd);
//
// This transform is necessary to compensate for transforms that the stream
// content producer may implicitly apply to the content. By forcing users of
- // a SurfaceTexture to apply this transform we avoid performing an extra
+ // a GLConsumer to apply this transform we avoid performing an extra
// copy of the data that would be needed to hide the transform from the
// user.
//
// doGLFenceWait inserts a wait command into the OpenGL ES command stream
// to ensure that it is safe for future OpenGL ES commands to access the
- // current texture buffer. This must be called each time updateTexImage
- // is called before issuing OpenGL ES commands that access the texture.
+ // current texture buffer.
status_t doGLFenceWait() const;
- // isSynchronousMode returns whether the SurfaceTexture is currently in
+ // isSynchronousMode returns whether the GLConsumer is currently in
// synchronous mode.
bool isSynchronousMode() const;
- // set the name of the SurfaceTexture that will be used to identify it in
+ // set the name of the GLConsumer that will be used to identify it in
// log messages.
void setName(const String8& name);
virtual status_t setSynchronousMode(bool enabled);
// getBufferQueue returns the BufferQueue object to which this
- // SurfaceTexture is connected.
+ // GLConsumer is connected.
sp<BufferQueue> getBufferQueue() const {
return mBufferQueue;
}
- // detachFromContext detaches the SurfaceTexture from the calling thread's
+ // detachFromContext detaches the GLConsumer from the calling thread's
// current OpenGL ES context. This context must be the same as the context
// that was current for previous calls to updateTexImage.
//
- // Detaching a SurfaceTexture from an OpenGL ES context will result in the
+ // Detaching a GLConsumer from an OpenGL ES context will result in the
// deletion of the OpenGL ES texture object into which the images were being
- // streamed. After a SurfaceTexture has been detached from the OpenGL ES
+ // streamed. After a GLConsumer has been detached from the OpenGL ES
// context calls to updateTexImage will fail returning INVALID_OPERATION
- // until the SurfaceTexture is attached to a new OpenGL ES context using the
+ // until the GLConsumer is attached to a new OpenGL ES context using the
// attachToContext method.
status_t detachFromContext();
- // attachToContext attaches a SurfaceTexture that is currently in the
- // 'detached' state to the current OpenGL ES context. A SurfaceTexture is
+ // attachToContext attaches a GLConsumer that is currently in the
+ // 'detached' state to the current OpenGL ES context. A GLConsumer is
// in the 'detached' state iff detachFromContext has successfully been
// called and no calls to attachToContext have succeeded since the last
// detachFromContext call. Calls to attachToContext made on a
- // SurfaceTexture that is not in the 'detached' state will result in an
+ // GLConsumer that is not in the 'detached' state will result in an
// INVALID_OPERATION error.
//
// The tex argument specifies the OpenGL ES texture object name in the
// mCurrentTextureBuf in addition to the ConsumerBase behavior.
virtual void abandonLocked();
- // dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
+ // dumpLocked overrides the ConsumerBase method to dump GLConsumer-
// specific info in addition to the ConsumerBase behavior.
virtual void dumpLocked(String8& result, const char* prefix, char* buffer,
size_t size) const;
virtual status_t releaseBufferLocked(int buf, EGLDisplay display,
EGLSyncKHR eglFence);
+ status_t releaseBufferLocked(int buf, EGLSyncKHR eglFence) {
+ return releaseBufferLocked(buf, mEglDisplay, eglFence);
+ }
+
static bool isExternalFormat(uint32_t format);
-private:
- // this version of updateTexImage() takes a functor used to reject or not
- // the newly acquired buffer.
- // this API is TEMPORARY and intended to be used by SurfaceFlinger only,
- // which is why class Layer is made a friend of SurfaceTexture below.
- class BufferRejecter {
- friend class SurfaceTexture;
- virtual bool reject(const sp<GraphicBuffer>& buf,
- const BufferQueue::BufferItem& item) = 0;
- protected:
- virtual ~BufferRejecter() { }
- };
- friend class Layer;
- status_t updateTexImage(BufferRejecter* rejecter, bool skipSync);
+ // This releases the buffer in the slot referenced by mCurrentTexture,
+ // then updates state to refer to the BufferItem, which must be a
+ // newly-acquired buffer.
+ status_t releaseAndUpdateLocked(const BufferQueue::BufferItem& item);
+
+ // Binds mTexName and the current buffer to mTexTarget. Uses
+ // mCurrentTexture if it's set, mCurrentTextureBuf if not. If the
+ // bind succeeds, this calls doGLFenceWait.
+ status_t bindTextureImageLocked();
+
+ // Gets the current EGLDisplay and EGLContext values, and compares them
+ // to mEglDisplay and mEglContext. If the fields have been previously
+ // set, the values must match; if not, the fields are set to the current
+ // values.
+ status_t checkAndUpdateEglStateLocked();
+ // If set, GLConsumer will use the EGL_ANDROID_native_fence_sync
+ // extension to create Android native fences for GLES activity.
+ static const bool sUseNativeFenceSync;
+
+private:
// createImage creates a new EGLImage from a GraphicBuffer.
EGLImageKHR createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer);
// doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
// stream to ensure that it is safe for future OpenGL ES commands to
- // access the current texture buffer. This must be called each time
- // updateTexImage is called before issuing OpenGL ES commands that access
- // the texture.
+ // access the current texture buffer.
status_t doGLFenceWaitLocked() const;
// syncForReleaseLocked performs the synchronization needed to release the
// before the outstanding accesses have completed.
status_t syncForReleaseLocked(EGLDisplay dpy);
- // The default consumer usage flags that SurfaceTexture always sets on its
+ // Normally, when we bind a buffer to a texture target, we bind a buffer
+ // that is referenced by an entry in mEglSlots. In some situations we
+ // have a buffer in mCurrentTextureBuf, but no corresponding entry for
+ // it in our slot array. bindUnslottedBuffer handles that situation by
+ // binding the buffer without touching the EglSlots.
+ status_t bindUnslottedBufferLocked(EGLDisplay dpy);
+
+ // The default consumer usage flags that GLConsumer always sets on its
// BufferQueue instance; these will be OR:d with any additional flags passed
- // from the SurfaceTexture user. In particular, SurfaceTexture will always
+ // from the GLConsumer user. In particular, GLConsumer will always
// consume buffers as hardware textures.
static const uint32_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
const GLenum mTexTarget;
// EGLSlot contains the information and object references that
- // SurfaceTexture maintains about a BufferQueue buffer slot.
- struct EGLSlot {
- EGLSlot()
+ // GLConsumer maintains about a BufferQueue buffer slot.
+ struct EglSlot {
+ EglSlot()
: mEglImage(EGL_NO_IMAGE_KHR),
mEglFence(EGL_NO_SYNC_KHR) {
}
EGLSyncKHR mEglFence;
};
- // mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
+ // mEglDisplay is the EGLDisplay with which this GLConsumer is currently
// associated. It is intialized to EGL_NO_DISPLAY and gets set to the
// current display when updateTexImage is called for the first time and when
// attachToContext is called.
EGLDisplay mEglDisplay;
- // mEglContext is the OpenGL ES context with which this SurfaceTexture is
+ // mEglContext is the OpenGL ES context with which this GLConsumer is
// currently associated. It is initialized to EGL_NO_CONTEXT and gets set
// to the current GL context when updateTexImage is called for the first
// time and when attachToContext is called.
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
- EGLSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
+ EglSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
// mCurrentTexture is the buffer slot index of the buffer that is currently
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
// ----------------------------------------------------------------------------
}; // namespace android
-#endif // ANDROID_GUI_SURFACETEXTURE_H
+#endif // ANDROID_GUI_CONSUMER_H
* limitations under the License.
*/
-#ifndef ANDROID_GUI_ISURFACETEXTURE_H
-#define ANDROID_GUI_ISURFACETEXTURE_H
+#ifndef ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
+#define ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
#include <stdint.h>
#include <sys/types.h>
class SurfaceTextureClient;
-class ISurfaceTexture : public IInterface
+/*
+ * This class defines the Binder IPC interface for the producer side of
+ * a queue of graphics buffers. It's used to send graphics data from one
+ * component to another. For example, a class that decodes video for
+ * playback might use this to provide frames. This is typically done
+ * indirectly, through SurfaceTextureClient.
+ *
+ * The underlying mechanism is a BufferQueue, which implements
+ * BnGraphicBufferProducer. In normal operation, the producer calls
+ * dequeueBuffer() to get an empty buffer, fills it with data, then
+ * calls queueBuffer() to make it available to the consumer.
+ *
+ * This class was previously called ISurfaceTexture.
+ */
+class IGraphicBufferProducer : public IInterface
{
public:
- DECLARE_META_INTERFACE(SurfaceTexture);
+ DECLARE_META_INTERFACE(GraphicBufferProducer);
enum {
BUFFER_NEEDS_REALLOCATION = 0x1,
};
// requestBuffer requests a new buffer for the given index. The server (i.e.
- // the ISurfaceTexture implementation) assigns the newly created buffer to
- // the given slot index, and the client is expected to mirror the
+ // the IGraphicBufferProducer implementation) assigns the newly created
+ // buffer to the given slot index, and the client is expected to mirror the
// slot->buffer mapping so that it's not necessary to transfer a
// GraphicBuffer for every dequeue operation.
virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
// The default mode is asynchronous.
virtual status_t setSynchronousMode(bool enabled) = 0;
- // connect attempts to connect a client API to the SurfaceTexture. This
- // must be called before any other ISurfaceTexture methods are called except
- // for getAllocator.
+ // connect attempts to connect a client API to the IGraphicBufferProducer.
+ // This must be called before any other IGraphicBufferProducer methods are
+ // called except for getAllocator.
//
// This method will fail if the connect was previously called on the
- // SurfaceTexture and no corresponding disconnect call was made.
+ // IGraphicBufferProducer and no corresponding disconnect call was made.
//
// outWidth, outHeight and outTransform are filled with the default width
// and height of the window and current transform applied to buffers,
// respectively.
virtual status_t connect(int api, QueueBufferOutput* output) = 0;
- // disconnect attempts to disconnect a client API from the SurfaceTexture.
- // Calling this method will cause any subsequent calls to other
- // ISurfaceTexture methods to fail except for getAllocator and connect.
- // Successfully calling connect after this will allow the other methods to
- // succeed again.
+ // disconnect attempts to disconnect a client API from the
+ // IGraphicBufferProducer. Calling this method will cause any subsequent
+ // calls to other IGraphicBufferProducer methods to fail except for
+ // getAllocator and connect. Successfully calling connect after this will
+ // allow the other methods to succeed again.
//
- // This method will fail if the the SurfaceTexture is not currently
+ // This method will fail if the the IGraphicBufferProducer is not currently
// connected to the specified client API.
virtual status_t disconnect(int api) = 0;
};
// ----------------------------------------------------------------------------
-class BnSurfaceTexture : public BnInterface<ISurfaceTexture>
+class BnGraphicBufferProducer : public BnInterface<IGraphicBufferProducer>
{
public:
virtual status_t onTransact( uint32_t code,
// ----------------------------------------------------------------------------
}; // namespace android
-#endif // ANDROID_GUI_ISURFACETEXTURE_H
+#endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
typedef int32_t SurfaceID;
-class ISurfaceTexture;
+class IGraphicBufferProducer;
class ISurface : public IInterface
{
public:
DECLARE_META_INTERFACE(Surface);
- virtual sp<ISurfaceTexture> getSurfaceTexture() const = 0;
+ virtual sp<IGraphicBufferProducer> getSurfaceTexture() const = 0;
};
// ----------------------------------------------------------------------------
class IDisplayEventConnection;
class IMemoryHeap;
+/*
+ * This class defines the Binder IPC interface for accessing various
+ * SurfaceFlinger features.
+ */
class ISurfaceComposer: public IInterface {
public:
DECLARE_META_INTERFACE(SurfaceComposer);
*/
virtual void bootFinished() = 0;
- /* verify that an ISurfaceTexture was created by SurfaceFlinger.
+ /* verify that an IGraphicBufferProducer was created by SurfaceFlinger.
*/
virtual bool authenticateSurfaceTexture(
- const sp<ISurfaceTexture>& surface) const = 0;
+ const sp<IGraphicBufferProducer>& surface) const = 0;
/* Capture the specified screen. requires READ_FRAME_BUFFER permission
* This function will fail if there is a secure window on screen.
// ---------------------------------------------------------------------------
-class ISurfaceTexture;
+class IGraphicBufferProducer;
class Surface;
class SurfaceComposerClient;
// ---------------------------------------------------------------------------
+/*
+ * This is a small wrapper around SurfaceTextureClient.
+ *
+ * TODO: rename and/or merge with STC.
+ */
class Surface : public SurfaceTextureClient
{
public:
uint32_t reserved[2];
};
- explicit Surface(const sp<ISurfaceTexture>& st);
+ explicit Surface(const sp<IGraphicBufferProducer>& bp);
static status_t writeToParcel(const sp<Surface>& control, Parcel* parcel);
bool isValid();
uint32_t getIdentity() const { return mIdentity; }
- sp<ISurfaceTexture> getSurfaceTexture();
+ sp<IGraphicBufferProducer> getSurfaceTexture(); // TODO: rename this
// the lock/unlock APIs must be used from the same thread
status_t lock(SurfaceInfo* info, Region* dirty = NULL);
/*
* private stuff...
*/
- void init(const sp<ISurfaceTexture>& surfaceTexture);
+ void init(const sp<IGraphicBufferProducer>& bufferProducer);
static void cleanCachedSurfacesLocked();
class Composer;
class IMemoryHeap;
class ISurfaceComposerClient;
-class ISurfaceTexture;
+class IGraphicBufferProducer;
class Region;
// ---------------------------------------------------------------------------
status_t destroySurface(SurfaceID sid);
static void setDisplaySurface(const sp<IBinder>& token,
- const sp<ISurfaceTexture>& surface);
+ const sp<IGraphicBufferProducer>& bufferProducer);
static void setDisplayLayerStack(const sp<IBinder>& token,
uint32_t layerStack);
#ifndef ANDROID_GUI_SURFACETEXTURECLIENT_H
#define ANDROID_GUI_SURFACETEXTURECLIENT_H
-#include <gui/ISurfaceTexture.h>
-#include <gui/SurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/GLConsumer.h>
#include <gui/BufferQueue.h>
#include <ui/ANativeObjectBase.h>
class Surface;
+/*
+ * An implementation of ANativeWindow that feeds graphics buffers into a
+ * BufferQueue.
+ *
+ * This is typically used by programs that want to render frames through
+ * some means (maybe OpenGL, a software renderer, or a hardware decoder)
+ * and have the frames they create forwarded to SurfaceFlinger for
+ * compositing. For example, a video decoder could render a frame and call
+ * eglSwapBuffers(), which invokes ANativeWindow callbacks defined by
+ * SurfaceTextureClient. STC then forwards the buffers through Binder IPC
+ * to the BufferQueue's producer interface, providing the new frame to a
+ * consumer such as GLConsumer.
+ *
+ * TODO: rename to Surface after merging or renaming the existing Surface
+ * class.
+ */
class SurfaceTextureClient
: public ANativeObjectBase<ANativeWindow, SurfaceTextureClient, RefBase>
{
public:
- SurfaceTextureClient(const sp<ISurfaceTexture>& surfaceTexture);
-
- // SurfaceTextureClient is overloaded to assist in refactoring ST and BQ.
- // SurfaceTexture is no longer an ISurfaceTexture, so client code
- // calling the original constructor will fail. Thus this convenience method
- // passes in the surfaceTexture's bufferQueue to the init method.
- SurfaceTextureClient(const sp<SurfaceTexture>& surfaceTexture);
+ SurfaceTextureClient(const sp<IGraphicBufferProducer>& bufferProducer);
- sp<ISurfaceTexture> getISurfaceTexture() const;
+ sp<IGraphicBufferProducer> getISurfaceTexture() const; // TODO: rename
protected:
SurfaceTextureClient();
virtual ~SurfaceTextureClient();
- void setISurfaceTexture(const sp<ISurfaceTexture>& surfaceTexture);
+ void setISurfaceTexture(const sp<IGraphicBufferProducer>& bufferProducer);
private:
// can't be copied
// mSurfaceTexture is the interface to the surface texture server. All
// operations on the surface texture client ultimately translate into
// interactions with the server using this interface.
- sp<ISurfaceTexture> mSurfaceTexture;
+ // TODO: rename to mBufferProducer
+ sp<IGraphicBufferProducer> mSurfaceTexture;
// mSlots stores the buffers that have been allocated for each buffer slot.
// It is initialized to null pointers, and gets filled in with the result of
- // ISurfaceTexture::requestBuffer when the client dequeues a buffer from a
+ // IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
mutable bool mConsumerRunningBehind;
// mMutex is the mutex used to prevent concurrent access to the member
- // variables of SurfaceTexture objects. It must be locked whenever the
+ // variables of SurfaceTextureClient objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
--- /dev/null
+/*
+ * Copyright (C) 2012 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 DRM_CLIENT_API_H_
+#define DRM_CLIENT_API_H_
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/List.h>
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+ // A DrmMessageStatus object aggregates a sessionId, which uniquely
+ // identifies a playback context with a status code and opaque message
+ // data.
+ struct DrmMessageStatus {
+ Vector<uint8_t> mSessionId;
+ status_t mStatus;
+ Vector<uint8_t> mData;
+ };
+
+ class DrmClientPlugin {
+ public:
+
+ // A license can be for downloaded, offline content or for online streaming
+ // Offline licenses are persisted on the device and may be used when the device
+ // is disconnected from the network.
+ enum LicenseType {
+ kLicenseType_Offline,
+ kLicenseType_Streaming
+ };
+
+ DrmClientPlugin() {}
+ virtual ~DrmClientPlugin() {}
+
+ // A license request/response exchange occurs between the app and a License
+ // Server to obtain the keys required to decrypt the content. getLicenseRequest()
+ // is used to obtain an opaque license request blob that is delivered to the
+ // license server.
+ //
+ // The init data passed to getLicenseRequest is container-specific and its
+ // meaning is interpreted based on the mime type provided in the mimeType
+ // parameter to getLicenseRequest. It could contain, for example, the content
+ // ID, key ID or other data obtained from the content metadata that is required
+ // in generating the license request.
+ //
+ // The DrmMessageStatus returned from getLicenseRequest contains a sessionId for
+ // the new session, a status code indicating whether the operation was successful
+ // and if so, the request blob is placed into the mData field.
+ virtual DrmMessageStatus getLicenseRequest(Vector<uint8_t> const &initData,
+ String8 const &mimeType, LicenseType licenseType) = 0;
+
+ // After a license response is received by the app, it is provided to the
+ // DrmClient plugin using provideLicenseResponse. The response data is provided
+ // in the mData field of the response parameter.
+ virtual status_t provideLicenseResponse(DrmMessageStatus const &response) = 0;
+
+ // Remove the keys associated with a license and release the session
+ virtual status_t clearLicense(Vector<uint8_t> const &sessionId) = 0;
+
+ // A provision request/response exchange occurs between the app and a
+ // provisioning server to retrieve a device certificate. getProvisionRequest
+ // is used to obtain an opaque license request blob that is delivered to the
+ // provisioning server.
+ //
+ // The DrmMessageStatus returned from getLicenseRequest contains a status code
+ // indicating whether the operation was successful and if so, the request blob
+ // is placed into the mData field.
+ virtual DrmMessageStatus getProvisionRequest() = 0;
+
+ // After a provision response is received by the app, it is provided to the
+ // DrmClient plugin using provideProvisionResponse. The response data is
+ // provided in the mData field of the response parameter.
+ virtual status_t provideProvisionResponse(DrmMessageStatus const &response) = 0;
+
+ // A means of enforcing the contractual requirement for a concurrent stream
+ // limit per subscriber across devices is provided via SecureStop. SecureStop
+ // is a means of securely monitoring the lifetime of sessions. Since playback
+ // on a device can be interrupted due to reboot, power failure, etc. a means
+ // of persisting the lifetime information on the device is needed.
+ //
+ // A signed version of the sessionID is written to persistent storage on the
+ // device when each MediaCrypto object is created. The sessionID is signed by
+ // the device private key to prevent tampering.
+ //
+ // In the normal case, playback will be completed, the session destroyed and
+ // the Secure Stops will be queried. The App queries secure stops and forwards
+ // the secure stop message to the server which verifies the signature and
+ // notifies the server side database that the session destruction has been
+ // confirmed. The persisted record on the client is only removed after positive
+ // confirmation that the server received the message using releaseSecureStops().
+ virtual List<DrmMessageStatus> getSecureStops() = 0;
+ virtual status_t releaseSecureStops(DrmMessageStatus const &ssRelease) = 0;
+
+ // Retrieve the device unique identifier for this device. The device unique
+ // identifier is established during device provisioning.
+ virtual Vector<uint8_t> getDeviceUniqueId() const = 0;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(DrmClientPlugin);
+ };
+
+} // namespace android
+
+#endif // DRM_CLIENT_API_H_
--- /dev/null
+/*
+ * Copyright (C) 2012 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 DRM_ENGINE_API_H_
+#define DRM_ENGINE_API_H_
+
+#include <utils/Errors.h>
+#include <media/stagefright/foundation/ABase.h>
+
+
+namespace android {
+
+ class CryptoPlugin;
+ class DrmClientPlugin;
+
+ // DRMs are implemented in DrmEngine plugins, which are dynamically
+ // loadable shared libraries that implement the entry point
+ // createDrmPluginFactory. createDrmPluginFactory constructs and returns
+ // an instance of a DrmPluginFactory object. When a MediaCrypto or
+ // DrmClient object needs to be constructed, all available
+ // DrmEngines present in the plugins directory on the device are scanned
+ // for a matching DrmEngine that can support the crypto scheme. When a
+ // match is found, the DrmEngine’s createCryptoPlugin or
+ // createDrmClientPlugin methods are used to create CryptoPlugin or
+ // DrmClientPlugin instances to support that DRM scheme.
+
+ class DrmPluginFactory {
+ public:
+ DrmPluginFactory() {}
+ virtual ~DrmPluginFactory() {}
+
+ // DrmPluginFactory::isCryptoSchemeSupported can be called to determine
+ // if the plugin factory is able to construct plugins that support a
+ // given crypto scheme, which is specified by a UUID.
+ virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) const = 0;
+
+ // Construct a CryptoPlugin for the crypto scheme specified by UUID.
+ // {data, size} provide scheme-specific initialization data.
+ virtual status_t createCryptoPlugin(
+ const uint8_t uuid[16], const void *data, size_t size,
+ CryptoPlugin **plugin) = 0;
+
+ // Construct a DrmClientPlugin for the crypto scheme specified by UUID.
+ // {data, size} provide scheme-specific initialization data.
+ virtual status_t createDrmClientPlugin(
+ const uint8_t uuid[16], const void *data, size_t size,
+ DrmClientPlugin **plugin) = 0;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(DrmPluginFactory);
+ };
+
+} // namespace android
+
+ // Loadable DrmEngine shared libraries should define the entry point
+ // createDrmPluginFactory as shown below:
+ //
+ // extern "C" {
+ // extern android::DrmPluginFactory *createDrmPluginFactory();
+ // }
+
+#endif // DRM_ENGINE_API_H_
namespace android {
+// Two different kinds of modules are covered under the same HDCPModule
+// structure below, a module either implements decryption or encryption.
struct HDCPModule {
typedef void (*ObserverFunc)(void *cookie, int msg, int ext1, int ext2);
// i.e. the HDCP session is now fully setup (AKE, Locality Check,
// SKE and any authentication with repeaters completed) or failed.
// ext1 should be a suitable error code (status_t), ext2 is
- // unused.
+ // unused for ENCRYPTION and in the case of HDCP_INITIALIZATION_COMPLETE
+ // holds the local TCP port the module is listening on.
HDCP_INITIALIZATION_COMPLETE,
HDCP_INITIALIZATION_FAILED,
HDCP_REVOKED_CONNECTION,
HDCP_TOPOLOGY_EXECEEDED,
HDCP_UNKNOWN_ERROR,
+
+ // DECRYPTION only: Indicates that a client has successfully connected,
+ // a secure session established and the module is ready to accept
+ // future calls to "decrypt".
+ HDCP_SESSION_ESTABLISHED,
};
// Module can call the notification function to signal completion/failure
virtual ~HDCPModule() {};
- // Request to setup an HDCP session with the specified host listening
- // on the specified port.
- virtual status_t initAsync(const char *host, unsigned port) = 0;
+ // ENCRYPTION: Request to setup an HDCP session with the host specified
+ // by addr and listening on the specified port.
+ // DECRYPTION: Request to setup an HDCP session, addr is the interface
+ // address the module should bind its socket to. port will be 0.
+ // The module will pick the port to listen on itself and report its choice
+ // in the "ext2" argument of the HDCP_INITIALIZATION_COMPLETE callback.
+ virtual status_t initAsync(const char *addr, unsigned port) = 0;
// Request to shutdown the active HDCP session.
virtual status_t shutdownAsync() = 0;
- // Encrypt a data according to the HDCP spec. The data is to be
- // encrypted in-place, only size bytes of data should be read/write,
- // even if the size is not a multiple of 128 bit (16 bytes).
+ // ENCRYPTION only:
+ // Encrypt data according to the HDCP spec. "size" bytes of data are
+ // available at "inData" (virtual address), "size" may not be a multiple
+ // of 128 bits (16 bytes). An equal number of encrypted bytes should be
+ // written to the buffer at "outData" (virtual address).
// This operation is to be synchronous, i.e. this call does not return
// until outData contains size bytes of encrypted data.
// streamCTR will be assigned by the caller (to 0 for the first PES stream,
// 1 for the second and so on)
- // inputCTR will be maintained by the callee for each PES stream.
+ // inputCTR _will_be_maintained_by_the_callee_ for each PES stream.
virtual status_t encrypt(
const void *inData, size_t size, uint32_t streamCTR,
- uint64_t *outInputCTR, void *outData) = 0;
+ uint64_t *outInputCTR, void *outData) {
+ return INVALID_OPERATION;
+ }
+
+ // DECRYPTION only:
+ // Decrypt data according to the HDCP spec.
+ // "size" bytes of encrypted data are available at "inData"
+ // (virtual address), "size" may not be a multiple of 128 bits (16 bytes).
+ // An equal number of decrypted bytes should be written to the buffer
+ // at "outData" (virtual address).
+ // This operation is to be synchronous, i.e. this call does not return
+ // until outData contains size bytes of decrypted data.
+ // Both streamCTR and inputCTR will be provided by the caller.
+ virtual status_t decrypt(
+ const void *inData, size_t size,
+ uint32_t streamCTR, uint64_t inputCTR,
+ void *outData) {
+ return INVALID_OPERATION;
+ }
private:
HDCPModule(const HDCPModule &);
} // namespace android
-// A shared library exporting the following method should be included to
+// A shared library exporting the following methods should be included to
// support HDCP functionality. The shared library must be called
// "libstagefright_hdcp.so", it will be dynamically loaded into the
// mediaserver process.
extern "C" {
+ // Create a module for ENCRYPTION.
extern android::HDCPModule *createHDCPModule(
void *cookie, android::HDCPModule::ObserverFunc);
+
+ // Create a module for DECRYPTION.
+ extern android::HDCPModule *createHDCPModuleForDecryption(
+ void *cookie, android::HDCPModule::ObserverFunc);
}
#endif // HDCP_API_H_
OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100,
OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00,
OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03,
+ OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002,
OMX_COLOR_FormatMax = 0x7FFFFFFF
} OMX_COLOR_FORMATTYPE;
--- /dev/null
+/*
+ * Copyright (c) 2010 The Khronos Group Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject
+ * to the following conditions:
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/** @file OMX_IndexExt.h - OpenMax IL version 1.1.2
+ * The OMX_IndexExt header file contains extensions to the definitions
+ * for both applications and components .
+ */
+
+#ifndef OMX_IndexExt_h
+#define OMX_IndexExt_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Each OMX header shall include all required header files to allow the
+ * header to compile without errors. The includes below are required
+ * for this header file to compile successfully
+ */
+#include <OMX_Index.h>
+
+
+/** Khronos standard extension indices.
+
+This enum lists the current Khronos extension indices to OpenMAX IL.
+*/
+typedef enum OMX_INDEXEXTTYPE {
+
+ /* Component parameters and configurations */
+ OMX_IndexExtComponentStartUnused = OMX_IndexKhronosExtensions + 0x00100000,
+ OMX_IndexConfigCallbackRequest, /**< reference: OMX_CONFIG_CALLBACKREQUESTTYPE */
+ OMX_IndexConfigCommitMode, /**< reference: OMX_CONFIG_COMMITMODETYPE */
+ OMX_IndexConfigCommit, /**< reference: OMX_CONFIG_COMMITTYPE */
+
+ /* Port parameters and configurations */
+ OMX_IndexExtPortStartUnused = OMX_IndexKhronosExtensions + 0x00200000,
+
+ /* Audio parameters and configurations */
+ OMX_IndexExtAudioStartUnused = OMX_IndexKhronosExtensions + 0x00400000,
+
+ /* Image parameters and configurations */
+ OMX_IndexExtImageStartUnused = OMX_IndexKhronosExtensions + 0x00500000,
+
+ /* Video parameters and configurations */
+ OMX_IndexExtVideoStartUnused = OMX_IndexKhronosExtensions + 0x00600000,
+ OMX_IndexParamNalStreamFormatSupported, /**< reference: OMX_NALSTREAMFORMATTYPE */
+ OMX_IndexParamNalStreamFormat, /**< reference: OMX_NALSTREAMFORMATTYPE */
+ OMX_IndexParamNalStreamFormatSelect, /**< reference: OMX_NALSTREAMFORMATTYPE */
+ OMX_IndexParamVideoVp8, /**< reference: OMX_VIDEO_PARAM_VP8TYPE */
+ OMX_IndexConfigVideoVp8ReferenceFrame, /**< reference: OMX_VIDEO_VP8REFERENCEFRAMETYPE */
+ OMX_IndexConfigVideoVp8ReferenceFrameType, /**< reference: OMX_VIDEO_VP8REFERENCEFRAMEINFOTYPE */
+
+ /* Image & Video common configurations */
+ OMX_IndexExtCommonStartUnused = OMX_IndexKhronosExtensions + 0x00700000,
+
+ /* Other configurations */
+ OMX_IndexExtOtherStartUnused = OMX_IndexKhronosExtensions + 0x00800000,
+
+ /* Time configurations */
+ OMX_IndexExtTimeStartUnused = OMX_IndexKhronosExtensions + 0x00900000,
+
+ OMX_IndexExtMax = 0x7FFFFFFF
+} OMX_INDEXEXTTYPE;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* OMX_IndexExt_h */
+/* File EOF */
--- /dev/null
+/*
+ * Copyright (c) 2010 The Khronos Group Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject
+ * to the following conditions:
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/** OMX_VideoExt.h - OpenMax IL version 1.1.2
+ * The OMX_VideoExt header file contains extensions to the
+ * definitions used by both the application and the component to
+ * access video items.
+ */
+
+#ifndef OMX_VideoExt_h
+#define OMX_VideoExt_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Each OMX header shall include all required header files to allow the
+ * header to compile without errors. The includes below are required
+ * for this header file to compile successfully
+ */
+#include <OMX_Core.h>
+
+/** NALU Formats */
+typedef enum OMX_NALUFORMATSTYPE {
+ OMX_NaluFormatStartCodes = 1,
+ OMX_NaluFormatOneNaluPerBuffer = 2,
+ OMX_NaluFormatOneByteInterleaveLength = 4,
+ OMX_NaluFormatTwoByteInterleaveLength = 8,
+ OMX_NaluFormatFourByteInterleaveLength = 16,
+ OMX_NaluFormatCodingMax = 0x7FFFFFFF
+} OMX_NALUFORMATSTYPE;
+
+/** NAL Stream Format */
+typedef struct OMX_NALSTREAMFORMATTYPE{
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_NALUFORMATSTYPE eNaluFormat;
+} OMX_NALSTREAMFORMATTYPE;
+
+/** Enum for standard video codingtype extensions */
+typedef enum OMX_VIDEO_CODINGEXTTYPE {
+ OMX_VIDEO_ExtCodingUnused = OMX_VIDEO_CodingKhronosExtensions,
+ OMX_VIDEO_CodingVP8, /**< VP8/WebM */
+} OMX_VIDEO_CODINGEXTTYPE;
+
+/** VP8 profiles */
+typedef enum OMX_VIDEO_VP8PROFILETYPE {
+ OMX_VIDEO_VP8ProfileMain = 0x01,
+ OMX_VIDEO_VP8ProfileUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_VP8ProfileMax = 0x7FFFFFFF
+} OMX_VIDEO_VP8PROFILETYPE;
+
+/** VP8 levels */
+typedef enum OMX_VIDEO_VP8LEVELTYPE {
+ OMX_VIDEO_VP8Level_Version0 = 0x01,
+ OMX_VIDEO_VP8Level_Version1 = 0x02,
+ OMX_VIDEO_VP8Level_Version2 = 0x04,
+ OMX_VIDEO_VP8Level_Version3 = 0x08,
+ OMX_VIDEO_VP8LevelUnknown = 0x6EFFFFFF,
+ OMX_VIDEO_VP8LevelMax = 0x7FFFFFFF
+} OMX_VIDEO_VP8LEVELTYPE;
+
+/** VP8 Param */
+typedef struct OMX_VIDEO_PARAM_VP8TYPE {
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_VIDEO_VP8PROFILETYPE eProfile;
+ OMX_VIDEO_VP8LEVELTYPE eLevel;
+ OMX_U32 nDCTPartitions;
+ OMX_BOOL bErrorResilientMode;
+} OMX_VIDEO_PARAM_VP8TYPE;
+
+/** Structure for configuring VP8 reference frames */
+typedef struct OMX_VIDEO_VP8REFERENCEFRAMETYPE {
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_BOOL bPreviousFrameRefresh;
+ OMX_BOOL bGoldenFrameRefresh;
+ OMX_BOOL bAlternateFrameRefresh;
+ OMX_BOOL bUsePreviousFrame;
+ OMX_BOOL bUseGoldenFrame;
+ OMX_BOOL bUseAlternateFrame;
+} OMX_VIDEO_VP8REFERENCEFRAMETYPE;
+
+/** Structure for querying VP8 reference frame type */
+typedef struct OMX_VIDEO_VP8REFERENCEFRAMEINFOTYPE {
+ OMX_U32 nSize;
+ OMX_VERSIONTYPE nVersion;
+ OMX_U32 nPortIndex;
+ OMX_BOOL bIsIntraFrame;
+ OMX_BOOL bIsGoldenOrAlternateFrame;
+} OMX_VIDEO_VP8REFERENCEFRAMEINFOTYPE;
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* OMX_VideoExt_h */
+/* File EOF */
uint32_t what;
sp<IBinder> token;
- sp<ISurfaceTexture> surface;
+ sp<IGraphicBufferProducer> surface;
uint32_t layerStack;
uint32_t orientation;
Rect viewport;
#include <ui/Rect.h>
#include <utils/Flattenable.h>
#include <utils/String8.h>
+#include <utils/Timers.h>
struct ANativeWindowBuffer;
public:
static const sp<Fence> NO_FENCE;
+ // TIMEOUT_NEVER may be passed to the wait method to indicate that it
+ // should wait indefinitely for the fence to signal.
+ enum { TIMEOUT_NEVER = -1 };
+
// Construct a new Fence object with an invalid file descriptor. This
// should be done when the Fence object will be set up by unflattening
// serialized data.
// the caller and will be included in the log message.
status_t waitForever(unsigned int warningTimeout, const char* logname);
- // TIMEOUT_NEVER may be passed to the wait method to indicate that it
- // should wait indefinitely for the fence to signal.
- enum { TIMEOUT_NEVER = -1 };
-
// merge combines two Fence objects, creating a new Fence object that
// becomes signaled when both f1 and f2 are signaled (even if f1 or f2 is
// destroyed before it becomes signaled). The name argument specifies the
// be returned and errno will indicate the problem.
int dup() const;
+ // getSignalTime returns the system monotonic clock time at which the
+ // fence transitioned to the signaled state. If the fence is not signaled
+ // then INT64_MAX is returned. If the fence is invalid or if an error
+ // occurs then -1 is returned.
+ nsecs_t getSignalTime() const;
+
// Flattenable interface
size_t getFlattenedSize() const;
size_t getFdCount() const;
static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
friend class Singleton<GraphicBufferAllocator>;
- friend class BufferLiberatorThread;
GraphicBufferAllocator();
~GraphicBufferAllocator();
BasicHashtableImpl::rehash(minimumCapacity, loadFactor);
}
+ /* Determines whether there is room to add another entry without rehashing.
+ * When this returns true, a subsequent add() operation is guaranteed to
+ * complete without performing a rehash.
+ */
+ inline bool hasMoreRoom() const {
+ return mCapacity > mFilledBuckets;
+ }
+
protected:
static inline const TEntry& entryFor(const Bucket& bucket) {
return reinterpret_cast<const TEntry&>(bucket.entry);
--- /dev/null
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/* Implementation of Jenkins one-at-a-time hash function. These choices are
+ * optimized for code size and portability, rather than raw speed. But speed
+ * should still be quite good.
+ **/
+
+#ifndef ANDROID_JENKINS_HASH_H
+#define ANDROID_JENKINS_HASH_H
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+/* The Jenkins hash of a sequence of 32 bit words A, B, C is:
+ * Whiten(Mix(Mix(Mix(0, A), B), C)) */
+
+inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) {
+ hash += data;
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ return hash;
+}
+
+hash_t JenkinsHashWhiten(uint32_t hash);
+
+/* Helpful utility functions for hashing data in 32 bit chunks */
+uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size);
+
+uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size);
+
+}
+
+#endif // ANDROID_JENKINS_HASH_H
--- /dev/null
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_LINEARALLOCATOR_H
+#define ANDROID_LINEARALLOCATOR_H
+
+#include <stddef.h>
+
+namespace android {
+
+/**
+ * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids
+ * the overhead of malloc when many objects are allocated. It is most useful when creating many
+ * small objects with a similar lifetime, and doesn't add significant overhead for large
+ * allocations.
+ */
+class LinearAllocator {
+public:
+ LinearAllocator();
+ ~LinearAllocator();
+
+ /**
+ * Reserves and returns a region of memory of at least size 'size', aligning as needed.
+ * Typically this is used in an object's overridden new() method or as a replacement for malloc.
+ *
+ * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling
+ * delete() on an object stored in a buffer is needed, it should be overridden to use
+ * rewindIfLastAlloc()
+ */
+ void* alloc(size_t size);
+
+ /**
+ * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its
+ * state if possible. No destructors are called.
+ */
+ void rewindIfLastAlloc(void* ptr, size_t allocSize);
+
+ /**
+ * Dump memory usage statistics to the log (allocated and wasted space)
+ */
+ void dumpMemoryStats(const char* prefix = "");
+
+ /**
+ * The number of bytes used for buffers allocated in the LinearAllocator (does not count space
+ * wasted)
+ */
+ size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
+
+private:
+ LinearAllocator(const LinearAllocator& other);
+
+ class Page;
+
+ Page* newPage(size_t pageSize);
+ bool fitsInCurrentPage(size_t size);
+ void ensureNext(size_t size);
+ void* start(Page *p);
+ void* end(Page* p);
+
+ size_t mPageSize;
+ size_t mMaxAllocSize;
+ void* mNext;
+ Page* mCurrentPage;
+ Page* mPages;
+
+ // Memory usage tracking
+ size_t mTotalAllocated;
+ size_t mWastedSpace;
+ size_t mPageCount;
+ size_t mDedicatedPageCount;
+};
+
+}; // namespace android
+
+#endif // ANDROID_LINEARALLOCATOR_H
--- /dev/null
+/*
+ * Copyright (C) 2012 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 ANDROID_UTILS_LRU_CACHE_H
+#define ANDROID_UTILS_LRU_CACHE_H
+
+#include <utils/BasicHashtable.h>
+#include <utils/GenerationCache.h>
+#include <utils/UniquePtr.h>
+
+namespace android {
+
+// OnEntryRemoved is defined in GenerationCache.h, but maybe should move here.
+
+template <typename TKey, typename TValue>
+class LruCache {
+public:
+ explicit LruCache(uint32_t maxCapacity);
+
+ enum Capacity {
+ kUnlimitedCapacity,
+ };
+
+ void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);
+ size_t size() const;
+ const TValue& get(const TKey& key);
+ bool put(const TKey& key, const TValue& value);
+ bool remove(const TKey& key);
+ bool removeOldest();
+ void clear();
+
+ class Iterator {
+ public:
+ Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIndex(-1) {
+ }
+
+ bool next() {
+ mIndex = mCache.mTable->next(mIndex);
+ return mIndex != -1;
+ }
+
+ size_t index() const {
+ return mIndex;
+ }
+
+ const TValue& value() const {
+ return mCache.mTable->entryAt(mIndex).value;
+ }
+
+ const TKey& key() const {
+ return mCache.mTable->entryAt(mIndex).key;
+ }
+ private:
+ const LruCache<TKey, TValue>& mCache;
+ size_t mIndex;
+ };
+
+private:
+ LruCache(const LruCache& that); // disallow copy constructor
+
+ struct Entry {
+ TKey key;
+ TValue value;
+ Entry* parent;
+ Entry* child;
+
+ Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) {
+ }
+ const TKey& getKey() const { return key; }
+ };
+
+ void attachToCache(Entry& entry);
+ void detachFromCache(Entry& entry);
+ void rehash(size_t newCapacity);
+
+ UniquePtr<BasicHashtable<TKey, Entry> > mTable;
+ OnEntryRemoved<TKey, TValue>* mListener;
+ Entry* mOldest;
+ Entry* mYoungest;
+ uint32_t mMaxCapacity;
+ TValue mNullValue;
+};
+
+// Implementation is here, because it's fully templated
+template <typename TKey, typename TValue>
+LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity),
+ mNullValue(NULL), mTable(new BasicHashtable<TKey, Entry>), mYoungest(NULL), mOldest(NULL),
+ mListener(NULL) {
+};
+
+template<typename K, typename V>
+void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
+ mListener = listener;
+}
+
+template <typename TKey, typename TValue>
+size_t LruCache<TKey, TValue>::size() const {
+ return mTable->size();
+}
+
+template <typename TKey, typename TValue>
+const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index == -1) {
+ return mNullValue;
+ }
+ Entry& entry = mTable->editEntryAt(index);
+ detachFromCache(entry);
+ attachToCache(entry);
+ return entry.value;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
+ if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {
+ removeOldest();
+ }
+
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index >= 0) {
+ return false;
+ }
+ if (!mTable->hasMoreRoom()) {
+ rehash(mTable->capacity() * 2);
+ }
+
+ // Would it be better to initialize a blank entry and assign key, value?
+ Entry initEntry(key, value);
+ index = mTable->add(hash, initEntry);
+ Entry& entry = mTable->editEntryAt(index);
+ attachToCache(entry);
+ return true;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::remove(const TKey& key) {
+ hash_t hash = hash_type(key);
+ ssize_t index = mTable->find(-1, hash, key);
+ if (index < 0) {
+ return false;
+ }
+ Entry& entry = mTable->editEntryAt(index);
+ if (mListener) {
+ (*mListener)(entry.key, entry.value);
+ }
+ detachFromCache(entry);
+ mTable->removeAt(index);
+ return true;
+}
+
+template <typename TKey, typename TValue>
+bool LruCache<TKey, TValue>::removeOldest() {
+ if (mOldest != NULL) {
+ return remove(mOldest->key);
+ // TODO: should probably abort if false
+ }
+ return false;
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::clear() {
+ if (mListener) {
+ for (Entry* p = mOldest; p != NULL; p = p->child) {
+ (*mListener)(p->key, p->value);
+ }
+ }
+ mYoungest = NULL;
+ mOldest = NULL;
+ mTable->clear();
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::attachToCache(Entry& entry) {
+ if (mYoungest == NULL) {
+ mYoungest = mOldest = &entry;
+ } else {
+ entry.parent = mYoungest;
+ mYoungest->child = &entry;
+ mYoungest = &entry;
+ }
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
+ if (entry.parent != NULL) {
+ entry.parent->child = entry.child;
+ } else {
+ mOldest = entry.child;
+ }
+ if (entry.child != NULL) {
+ entry.child->parent = entry.parent;
+ } else {
+ mYoungest = entry.parent;
+ }
+
+ entry.parent = NULL;
+ entry.child = NULL;
+}
+
+template <typename TKey, typename TValue>
+void LruCache<TKey, TValue>::rehash(size_t newCapacity) {
+ UniquePtr<BasicHashtable<TKey, Entry> > oldTable(mTable.release());
+ Entry* oldest = mOldest;
+
+ mOldest = NULL;
+ mYoungest = NULL;
+ mTable.reset(new BasicHashtable<TKey, Entry>(newCapacity));
+ for (Entry* p = oldest; p != NULL; p = p->child) {
+ put(p->key, p->value);
+ }
+}
+
+}
+
+#endif // ANDROID_UTILS_LRU_CACHE_H
inline Mutex::Mutex() {
pthread_mutex_init(&mMutex, NULL);
}
-inline Mutex::Mutex(const char* name) {
+inline Mutex::Mutex(__attribute__((unused)) const char* name) {
pthread_mutex_init(&mMutex, NULL);
}
-inline Mutex::Mutex(int type, const char* name) {
+inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {
if (type == SHARED) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
inline RWLock::RWLock() {
pthread_rwlock_init(&mRWLock, NULL);
}
-inline RWLock::RWLock(const char* name) {
+inline RWLock::RWLock(__attribute__((unused)) const char* name) {
pthread_rwlock_init(&mRWLock, NULL);
}
-inline RWLock::RWLock(int type, const char* name) {
+inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {
if (type == SHARED) {
pthread_rwlockattr_t attr;
pthread_rwlockattr_init(&attr);
{
public:
inline LightRefBase() : mCount(0) { }
- inline void incStrong(const void* id) const {
+ inline void incStrong(__attribute__((unused)) const void* id) const {
android_atomic_inc(&mCount);
}
- inline void decStrong(const void* id) const {
+ inline void decStrong(__attribute__((unused)) const void* id) const {
if (android_atomic_dec(&mCount) == 1) {
delete static_cast<const T*>(this);
}
#include <cutils/compiler.h>
#include <utils/threads.h>
+#include <cutils/trace.h>
-// The ATRACE_TAG macro can be defined before including this header to trace
-// using one of the tags defined below. It must be defined to one of the
-// following ATRACE_TAG_* macros. The trace tag is used to filter tracing in
-// userland to avoid some of the runtime cost of tracing when it is not desired.
-//
-// Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always
-// being enabled - this should ONLY be done for debug code, as userland tracing
-// has a performance cost even when the trace is not being recorded. Defining
-// ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result
-// in the tracing always being disabled.
-//
-// These tags must be kept in sync with frameworks/base/core/java/android/os/Trace.java.
-#define ATRACE_TAG_NEVER 0 // The "never" tag is never enabled.
-#define ATRACE_TAG_ALWAYS (1<<0) // The "always" tag is always enabled.
-#define ATRACE_TAG_GRAPHICS (1<<1)
-#define ATRACE_TAG_INPUT (1<<2)
-#define ATRACE_TAG_VIEW (1<<3)
-#define ATRACE_TAG_WEBVIEW (1<<4)
-#define ATRACE_TAG_WINDOW_MANAGER (1<<5)
-#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)
-#define ATRACE_TAG_SYNC_MANAGER (1<<7)
-#define ATRACE_TAG_AUDIO (1<<8)
-#define ATRACE_TAG_VIDEO (1<<9)
-#define ATRACE_TAG_CAMERA (1<<10)
-#define ATRACE_TAG_LAST ATRACE_TAG_CAMERA
+// See <cutils/trace.h> for more ATRACE_* macros.
-#define ATRACE_TAG_NOT_READY (1LL<<63) // Reserved for use during init
-
-#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
-
-#ifndef ATRACE_TAG
-#define ATRACE_TAG ATRACE_TAG_NEVER
-#elif ATRACE_TAG > ATRACE_TAG_LAST
-#error ATRACE_TAG must be defined to be one of the tags defined in utils/Trace.h
-#endif
-
-// ATRACE_CALL traces the beginning and end of the current function. To trace
-// the correct start and end times this macro should be the first line of the
-// function body.
-#define ATRACE_CALL() android::ScopedTrace ___tracer(ATRACE_TAG, __FUNCTION__)
-
-// ATRACE_NAME traces the beginning and end of the current function. To trace
-// the correct start and end times this macro should be the first line of the
-// function body.
+// ATRACE_NAME traces the beginning and end of the current scope. To trace
+// the correct start and end times this macro should be declared first in the
+// scope body.
#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name)
-
-// ATRACE_INT traces a named integer value. This can be used to track how the
-// value changes over time in a trace.
-#define ATRACE_INT(name, value) android::Tracer::traceCounter(ATRACE_TAG, name, value)
-
-// ATRACE_ENABLED returns true if the trace tag is enabled. It can be used as a
-// guard condition around more expensive trace calculations.
-#define ATRACE_ENABLED() android::Tracer::isTagEnabled(ATRACE_TAG)
+// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
+#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
namespace android {
-class Tracer {
-
-public:
-
- static uint64_t getEnabledTags() {
- initIfNeeded();
- return sEnabledTags;
- }
-
- static inline bool isTagEnabled(uint64_t tag) {
- initIfNeeded();
- return sEnabledTags & tag;
- }
-
- static inline void traceCounter(uint64_t tag, const char* name,
- int32_t value) {
- if (CC_UNLIKELY(isTagEnabled(tag))) {
- char buf[1024];
- snprintf(buf, 1024, "C|%d|%s|%d", getpid(), name, value);
- write(sTraceFD, buf, strlen(buf));
- }
- }
-
- static inline void traceBegin(uint64_t tag, const char* name) {
- if (CC_UNLIKELY(isTagEnabled(tag))) {
- char buf[1024];
- size_t len = snprintf(buf, 1024, "B|%d|%s", getpid(), name);
- write(sTraceFD, buf, len);
- }
- }
-
- static inline void traceEnd(uint64_t tag) {
- if (CC_UNLIKELY(isTagEnabled(tag))) {
- char buf = 'E';
- write(sTraceFD, &buf, 1);
- }
- }
-
-private:
-
- static inline void initIfNeeded() {
- if (!android_atomic_acquire_load(&sIsReady)) {
- init();
- }
- }
-
- static void changeCallback();
-
- // init opens the trace marker file for writing and reads the
- // atrace.tags.enableflags system property. It does this only the first
- // time it is run, using sMutex for synchronization.
- static void init();
-
- // retrieve the current value of the system property.
- static void loadSystemProperty();
-
- // sIsReady is a boolean value indicating whether a call to init() has
- // completed in this process. It is initialized to 0 and set to 1 when the
- // first init() call completes. It is set to 1 even if a failure occurred
- // in init (e.g. the trace marker file couldn't be opened).
- //
- // This should be checked by all tracing functions using an atomic acquire
- // load operation before calling init(). This check avoids the need to lock
- // a mutex each time a trace function gets called.
- static volatile int32_t sIsReady;
-
- // sTraceFD is the file descriptor used to write to the kernel's trace
- // buffer. It is initialized to -1 and set to an open file descriptor in
- // init() while a lock on sMutex is held.
- //
- // This should only be used by a trace function after init() has
- // successfully completed.
- static int sTraceFD;
-
- // sEnabledTags is the set of tag bits for which tracing is currently
- // enabled. It is initialized to 0 and set based on the
- // atrace.tags.enableflags system property in init() while a lock on sMutex
- // is held.
- //
- // This should only be used by a trace function after init() has
- // successfully completed.
- //
- // This value is only ever non-zero when tracing is initialized and sTraceFD is not -1.
- static uint64_t sEnabledTags;
-
- // sMutex is used to protect the execution of init().
- static Mutex sMutex;
-};
-
class ScopedTrace {
-
public:
- inline ScopedTrace(uint64_t tag, const char* name) :
- mTag(tag) {
- Tracer::traceBegin(mTag, name);
- }
+inline ScopedTrace(uint64_t tag, const char* name)
+ : mTag(tag) {
+ atrace_begin(mTag,name);
+}
- inline ~ScopedTrace() {
- Tracer::traceEnd(mTag);
- }
+inline ~ScopedTrace() {
+ atrace_end(mTag);
+}
private:
-
uint64_t mTag;
};
ANDROID_REINTERPRET_HASH(float, uint32_t)
ANDROID_REINTERPRET_HASH(double, uint64_t)
-template <typename T> inline hash_t hash_type(const T*& value) {
+template <typename T> inline hash_t hash_type(T* const & value) {
return hash_type(uintptr_t(value));
}
# we have the common sources, plus some device-specific stuff
sources := \
+ AppOpsManager.cpp \
Binder.cpp \
BpBinder.cpp \
+ IAppOpsCallback.cpp \
+ IAppOpsService.cpp \
IInterface.cpp \
IMemory.cpp \
IPCThreadState.cpp \
--- /dev/null
+/*
+ * Copyright (C) 2013 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 <binder/AppOpsManager.h>
+#include <binder/IServiceManager.h>
+
+#include <utils/SystemClock.h>
+
+namespace android {
+
+static String16 _appops("appops");
+
+AppOpsManager::AppOpsManager()
+{
+}
+
+sp<IAppOpsService> AppOpsManager::getService()
+{
+ int64_t startTime = 0;
+ mLock.lock();
+ sp<IAppOpsService> service = mService;
+ while (true) {
+ if (service == NULL || !service->asBinder()->isBinderAlive()) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(_appops);
+ if (binder == NULL) {
+ // Wait for the app ops service to come back...
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ ALOGI("Waiting for app ops service");
+ } else if ((uptimeMillis()-startTime) > 10000) {
+ ALOGW("Waiting too long for app ops service, giving up");
+ return NULL;
+ }
+ sleep(1);
+ } else {
+ service = interface_cast<IAppOpsService>(binder);
+ mService = service;
+ }
+ }
+ }
+ mLock.unlock();
+ return service;
+}
+
+int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
+{
+ sp<IAppOpsService> service = getService();
+ return service != NULL ? service->checkOperation(op, uid, callingPackage) : MODE_IGNORED;
+}
+
+int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ sp<IAppOpsService> service = getService();
+ return service != NULL ? service->noteOperation(op, uid, callingPackage) : MODE_IGNORED;
+}
+
+int32_t AppOpsManager::startOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ sp<IAppOpsService> service = getService();
+ return service != NULL ? service->startOperation(op, uid, callingPackage) : MODE_IGNORED;
+}
+
+void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
+ sp<IAppOpsService> service = getService();
+ if (service != NULL) {
+ service->finishOperation(op, uid, callingPackage);
+ }
+}
+
+void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName,
+ const sp<IAppOpsCallback>& callback) {
+ sp<IAppOpsService> service = getService();
+ if (service != NULL) {
+ service->startWatchingMode(op, packageName, callback);
+ }
+}
+
+void AppOpsManager::stopWatchingMode(const sp<IAppOpsCallback>& callback) {
+ sp<IAppOpsService> service = getService();
+ if (service != NULL) {
+ service->stopWatchingMode(callback);
+ }
+}
+
+}; // namespace android
--- /dev/null
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "AppOpsCallback"
+
+#include <binder/IAppOpsCallback.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpAppOpsCallback : public BpInterface<IAppOpsCallback>
+{
+public:
+ BpAppOpsCallback(const sp<IBinder>& impl)
+ : BpInterface<IAppOpsCallback>(impl)
+ {
+ }
+
+ virtual void opChanged(int32_t op, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsCallback::getInterfaceDescriptor());
+ data.writeInt32(op);
+ data.writeString16(packageName);
+ remote()->transact(OP_CHANGED_TRANSACTION, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AppOpsCallback, "com.android.internal.app.IAppOpsCallback");
+
+// ----------------------------------------------------------------------
+
+status_t BnAppOpsCallback::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case OP_CHANGED_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsCallback, data, reply);
+ int32_t op = data.readInt32();
+ String16 packageName = data.readString16();
+ opChanged(op, packageName);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
--- /dev/null
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "AppOpsService"
+
+#include <binder/IAppOpsService.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpAppOpsService : public BpInterface<IAppOpsService>
+{
+public:
+ BpAppOpsService(const sp<IBinder>& impl)
+ : BpInterface<IAppOpsService>(impl)
+ {
+ }
+
+ virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(CHECK_OPERATION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return 0;
+ return reply.readInt32() != 0;
+ }
+
+ virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return 0;
+ return reply.readInt32() != 0;
+ }
+
+ virtual int32_t startOperation(int32_t code, int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return 0;
+ return reply.readInt32() != 0;
+ }
+
+ virtual void finishOperation(int32_t code, int32_t uid, const String16& packageName) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(code);
+ data.writeInt32(uid);
+ data.writeString16(packageName);
+ remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
+ }
+
+ virtual void startWatchingMode(int32_t op, const String16& packageName,
+ const sp<IAppOpsCallback>& callback) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeInt32(op);
+ data.writeString16(packageName);
+ data.writeStrongBinder(callback->asBinder());
+ remote()->transact(START_WATCHING_MODE_TRANSACTION, data, &reply);
+ }
+
+ virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+ data.writeStrongBinder(callback->asBinder());
+ remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService");
+
+// ----------------------------------------------------------------------
+
+status_t BnAppOpsService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ //printf("AppOpsService received: "); data.print();
+ switch(code) {
+ case CHECK_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t code = data.readInt32();
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ int32_t res = checkOperation(code, uid, packageName);
+ reply->writeNoException();
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
+ case NOTE_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t code = data.readInt32();
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ int32_t res = noteOperation(code, uid, packageName);
+ reply->writeNoException();
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
+ case START_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t code = data.readInt32();
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ int32_t res = startOperation(code, uid, packageName);
+ reply->writeNoException();
+ reply->writeInt32(res);
+ return NO_ERROR;
+ } break;
+ case FINISH_OPERATION_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t code = data.readInt32();
+ int32_t uid = data.readInt32();
+ String16 packageName = data.readString16();
+ finishOperation(code, uid, packageName);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ case START_WATCHING_MODE_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ int32_t op = data.readInt32();
+ String16 packageName = data.readString16();
+ sp<IAppOpsCallback> callback = interface_cast<IAppOpsCallback>(data.readStrongBinder());
+ startWatchingMode(op, packageName, callback);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ case STOP_WATCHING_MODE_TRANSACTION: {
+ CHECK_INTERFACE(IAppOpsService, data, reply);
+ sp<IAppOpsCallback> callback = interface_cast<IAppOpsCallback>(data.readStrongBinder());
+ stopWatchingMode(callback);
+ reply->writeNoException();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
--- /dev/null
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdiskusage
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := dirsize.c
+
+include $(BUILD_STATIC_LIBRARY)
\ No newline at end of file
--- /dev/null
+/*
+ *
+ * Copyright (C) 2008, 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 <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <diskusage/dirsize.h>
+
+int64_t stat_size(struct stat *s)
+{
+ int64_t blksize = s->st_blksize;
+ int64_t size = s->st_size;
+
+ if (blksize) {
+ /* round up to filesystem block size */
+ size = (size + blksize - 1) & (~(blksize - 1));
+ }
+
+ return size;
+}
+
+int64_t calculate_dir_size(int dfd)
+{
+ int64_t size = 0;
+ struct stat s;
+ DIR *d;
+ struct dirent *de;
+
+ d = fdopendir(dfd);
+ if (d == NULL) {
+ close(dfd);
+ return 0;
+ }
+
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ size += stat_size(&s);
+ }
+ if (de->d_type == DT_DIR) {
+ int subfd;
+
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0)
+ continue;
+ if ((name[1] == '.') && (name[2] == 0))
+ continue;
+ }
+
+ subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+ if (subfd >= 0) {
+ size += calculate_dir_size(subfd);
+ }
+ }
+ }
+ closedir(d);
+ return size;
+}
LOCAL_SRC_FILES:= \
BitTube.cpp \
+ BufferItemConsumer.cpp \
BufferQueue.cpp \
ConsumerBase.cpp \
+ CpuConsumer.cpp \
DisplayEventReceiver.cpp \
+ DummyConsumer.cpp \
+ GLConsumer.cpp \
+ GraphicBufferAlloc.cpp \
+ GuiConfig.cpp \
IDisplayEventConnection.cpp \
+ IGraphicBufferAlloc.cpp \
+ IGraphicBufferProducer.cpp \
ISensorEventConnection.cpp \
ISensorServer.cpp \
- ISurfaceTexture.cpp \
- Sensor.cpp \
- SensorEventQueue.cpp \
- SensorManager.cpp \
- SurfaceTexture.cpp \
- SurfaceTextureClient.cpp \
- ISurfaceComposer.cpp \
ISurface.cpp \
+ ISurfaceComposer.cpp \
ISurfaceComposerClient.cpp \
- IGraphicBufferAlloc.cpp \
LayerState.cpp \
+ Sensor.cpp \
+ SensorEventQueue.cpp \
+ SensorManager.cpp \
Surface.cpp \
SurfaceComposerClient.cpp \
- DummyConsumer.cpp \
- CpuConsumer.cpp \
- BufferItemConsumer.cpp \
- GuiConfig.cpp
+ SurfaceTextureClient.cpp \
LOCAL_SHARED_LIBRARIES := \
libbinder \
return err;
}
- if (waitForFence && item->mFence.get()) {
+ if (waitForFence) {
err = item->mFence->waitForever(1000, "BufferItemConsumer::acquireBuffer");
if (err != OK) {
BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)",
#include <private/gui/ComposerService.h>
#include <utils/Log.h>
-#include <gui/SurfaceTexture.h>
#include <utils/Trace.h>
// Macros for including the BufferQueue name in log messages
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!");
+ ST_LOGE("setBufferCount: BufferQueue has been abandoned!");
return NO_INIT;
}
if (bufferCount > NUM_BUFFER_SLOTS) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("query: SurfaceTexture has been abandoned!");
+ ST_LOGE("query: BufferQueue has been abandoned!");
return NO_INIT;
}
ST_LOGV("requestBuffer: slot=%d", slot);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
+ ST_LOGE("requestBuffer: BufferQueue has been abandoned!");
return NO_INIT;
}
int maxBufferCount = getMaxBufferCountLocked();
bool tryAgain = true;
while (tryAgain) {
if (mAbandoned) {
- ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+ ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!");
return NO_INIT;
}
assert(mSlots[i].mBufferState == BufferSlot::FREE);
if (mSlots[i].mGraphicBuffer != NULL) {
freeBufferLocked(i);
- returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+ returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS;
}
}
h = mDefaultHeight;
}
- // buffer is now in DEQUEUED (but can also be current at the same time,
- // if we're in synchronous mode)
mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
mSlots[buf].mGraphicBuffer = NULL;
mSlots[buf].mRequestBufferCalled = false;
mSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
- mSlots[buf].mFence.clear();
+ mSlots[buf].mFence = Fence::NO_FENCE;
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
- returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+ returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION;
}
dpy = mSlots[buf].mEglDisplay;
eglFence = mSlots[buf].mEglFence;
outFence = mSlots[buf].mFence;
mSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
- mSlots[buf].mFence.clear();
+ mSlots[buf].mFence = Fence::NO_FENCE;
} // end lock scope
- if (returnFlags & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) {
+ if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
status_t error;
sp<GraphicBuffer> graphicBuffer(
mGraphicBufferAlloc->createGraphicBuffer(
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+ ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!");
return NO_INIT;
}
}
}
-
if (eglFence != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(dpy, eglFence, 0, 1000000000);
// If something goes wrong, log the error, but return the buffer without
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
+ ST_LOGE("setSynchronousMode: BufferQueue has been abandoned!");
return NO_INIT;
}
input.deflate(×tamp, &crop, &scalingMode, &transform, &fence);
+ if (fence == NULL) {
+ ST_LOGE("queueBuffer: fence is NULL");
+ return BAD_VALUE;
+ }
+
ST_LOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x "
"scale=%s",
buf, timestamp, crop.left, crop.top, crop.right, crop.bottom,
{ // scope for the lock
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!");
+ ST_LOGE("queueBuffer: BufferQueue has been abandoned!");
return NO_INIT;
}
int maxBufferCount = getMaxBufferCountLocked();
ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
buf, mSlots[buf].mBufferState);
return;
+ } else if (fence == NULL) {
+ ST_LOGE("cancelBuffer: fence is NULL");
+ return;
}
mSlots[buf].mBufferState = BufferSlot::FREE;
mSlots[buf].mFrameNumber = 0;
eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence);
mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
}
- mSlots[slot].mFence.clear();
+ mSlots[slot].mFence = Fence::NO_FENCE;
}
void BufferQueue::freeAllBuffersLocked() {
mSlots[buf].mAcquireCalled = true;
mSlots[buf].mNeedsCleanupOnRelease = false;
mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
- mSlots[buf].mFence.clear();
+ mSlots[buf].mFence = Fence::NO_FENCE;
mQueue.erase(front);
mDequeueCondition.broadcast();
Mutex::Autolock _l(mMutex);
- if (buf == INVALID_BUFFER_SLOT) {
- return -EINVAL;
+ if (buf == INVALID_BUFFER_SLOT || fence == NULL) {
+ return BAD_VALUE;
}
mSlots[buf].mEglDisplay = display;
status_t err = mBufferQueue->consumerConnect(proxy);
if (err != NO_ERROR) {
- CB_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)",
+ CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
} else {
mBufferQueue->setConsumerName(mName);
void ConsumerBase::freeBufferLocked(int slotIndex) {
CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
mSlots[slotIndex].mGraphicBuffer = 0;
- mSlots[slotIndex].mFence = 0;
+ mSlots[slotIndex].mFence = Fence::NO_FENCE;
}
// Used for refactoring, should not be in final interface
sp<FrameAvailableListener> listener;
{ // scope for the lock
Mutex::Autolock lock(mMutex);
- listener = mFrameAvailableListener;
+ listener = mFrameAvailableListener.promote();
}
if (listener != NULL) {
}
void ConsumerBase::setFrameAvailableListener(
- const sp<FrameAvailableListener>& listener) {
+ const wp<FrameAvailableListener>& listener) {
CB_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
freeBufferLocked(slot);
}
- mSlots[slot].mFence.clear();
+ mSlots[slot].mFence = Fence::NO_FENCE;
return err;
}
-} // namespace android
\ No newline at end of file
+} // namespace android
* limitations under the License.
*/
-#define LOG_TAG "SurfaceTexture"
+#define LOG_TAG "GLConsumer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceTexture.h>
+#include <gui/GLConsumer.h>
#include <private/gui/ComposerService.h>
#include <utils/String8.h>
#include <utils/Trace.h>
-// This compile option makes SurfaceTexture use the
+namespace android {
+
+// This compile option makes GLConsumer use the
// EGL_ANDROID_native_fence_sync extension to create Android native fences to
// signal when all GLES reads for a given buffer have completed. It is not
// compatible with using the EGL_KHR_fence_sync extension for the same
#ifdef USE_FENCE_SYNC
#error "USE_NATIVE_FENCE_SYNC and USE_FENCE_SYNC are incompatible"
#endif
-static const bool useNativeFenceSync = true;
+const bool GLConsumer::sUseNativeFenceSync = true;
#else
-static const bool useNativeFenceSync = false;
+const bool GLConsumer::sUseNativeFenceSync = false;
#endif
-// This compile option makes SurfaceTexture use the EGL_ANDROID_sync_wait
+// This compile option makes GLConsumer use the EGL_ANDROID_sync_wait
// extension to insert server-side waits into the GLES command stream. This
// feature requires the EGL_ANDROID_native_fence_sync and
// EGL_ANDROID_wait_sync extensions.
static const bool useWaitSync = false;
#endif
-// Macros for including the SurfaceTexture name in log messages
+// Macros for including the GLConsumer name in log messages
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
-namespace android {
-
// Transform matrices
static float mtxIdentity[16] = {
1, 0, 0, 0,
static void mtxMul(float out[16], const float a[16], const float b[16]);
-SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
+GLConsumer::GLConsumer(GLuint tex, bool allowSynchronousMode,
GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
mCurrentTransform(0),
+ mCurrentFence(Fence::NO_FENCE),
mCurrentTimestamp(0),
mFilteringEnabled(true),
mTexName(tex),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(true)
{
- ST_LOGV("SurfaceTexture");
+ ST_LOGV("GLConsumer");
memcpy(mCurrentTransformMatrix, mtxIdentity,
sizeof(mCurrentTransformMatrix));
mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}
-status_t SurfaceTexture::setDefaultMaxBufferCount(int bufferCount) {
+status_t GLConsumer::setDefaultMaxBufferCount(int bufferCount) {
Mutex::Autolock lock(mMutex);
return mBufferQueue->setDefaultMaxBufferCount(bufferCount);
}
-status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
+status_t GLConsumer::setDefaultBufferSize(uint32_t w, uint32_t h)
{
Mutex::Autolock lock(mMutex);
mDefaultWidth = w;
return mBufferQueue->setDefaultBufferSize(w, h);
}
-status_t SurfaceTexture::updateTexImage() {
- return SurfaceTexture::updateTexImage(NULL, false);
+status_t GLConsumer::updateTexImage() {
+ ATRACE_CALL();
+ ST_LOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ST_LOGE("updateTexImage: GLConsumer is abandoned!");
+ return NO_INIT;
+ }
+
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferQueue::BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = acquireBufferLocked(&item);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // We always bind the texture even if we don't update its contents.
+ ST_LOGV("updateTexImage: no buffers were available");
+ glBindTexture(mTexTarget, mTexName);
+ err = NO_ERROR;
+ } else {
+ ST_LOGE("updateTexImage: acquire failed: %s (%d)",
+ strerror(-err), err);
+ }
+ return err;
+ }
+
+ // Release the previous buffer.
+ err = releaseAndUpdateLocked(item);
+ if (err != NO_ERROR) {
+ // We always bind the texture.
+ glBindTexture(mTexTarget, mTexName);
+ return err;
+ }
+
+ // Bind the new buffer to the GL texture, and wait until it's ready.
+ return bindTextureImageLocked();
}
-status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
+status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item) {
status_t err = ConsumerBase::acquireBufferLocked(item);
if (err != NO_ERROR) {
return err;
int slot = item->mBuf;
if (item->mGraphicBuffer != NULL) {
+ // This buffer has not been acquired before, so we must assume
+ // that any EGLImage in mEglSlots is stale.
if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage);
+ if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
+ ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
+ slot);
+ // keep going
+ }
mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
}
}
- // Update the GL texture object. We may have to do this even when
- // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
- // detaching from a context but the buffer has not been re-allocated.
- if (mEglSlots[slot].mEglImage == EGL_NO_IMAGE_KHR) {
- EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer);
- if (image == EGL_NO_IMAGE_KHR) {
- return UNKNOWN_ERROR;
- }
- mEglSlots[slot].mEglImage = image;
- }
-
return NO_ERROR;
}
-status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display,
+status_t GLConsumer::releaseBufferLocked(int buf, EGLDisplay display,
EGLSyncKHR eglFence) {
- status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay,
- eglFence);
+ status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence);
mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
return err;
}
-status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) {
- ATRACE_CALL();
- ST_LOGV("updateTexImage");
- Mutex::Autolock lock(mMutex);
-
+status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
+{
status_t err = NO_ERROR;
- if (mAbandoned) {
- ST_LOGE("updateTexImage: SurfaceTexture is abandoned!");
- return NO_INIT;
- }
-
if (!mAttached) {
- ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL "
+ ST_LOGE("releaseAndUpdate: GLConsumer is not attached to an OpenGL "
"ES context");
return INVALID_OPERATION;
}
- EGLDisplay dpy = eglGetCurrentDisplay();
- EGLContext ctx = eglGetCurrentContext();
-
- if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) ||
- dpy == EGL_NO_DISPLAY) {
- ST_LOGE("updateTexImage: invalid current EGLDisplay");
- return INVALID_OPERATION;
+ // Confirm state.
+ err = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
}
- if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
- ctx == EGL_NO_CONTEXT) {
- ST_LOGE("updateTexImage: invalid current EGLContext");
- return INVALID_OPERATION;
+ int buf = item.mBuf;
+
+ // If the mEglSlot entry is empty, create an EGLImage for the gralloc
+ // buffer currently in the slot in ConsumerBase.
+ //
+ // We may have to do this even when item.mGraphicBuffer == NULL (which
+ // means the buffer was previously acquired), if we destroyed the
+ // EGLImage when detaching from a context but the buffer has not been
+ // re-allocated.
+ if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
+ EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer);
+ if (image == EGL_NO_IMAGE_KHR) {
+ ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d",
+ mEglDisplay, buf);
+ return UNKNOWN_ERROR;
+ }
+ mEglSlots[buf].mEglImage = image;
}
- mEglDisplay = dpy;
- mEglContext = ctx;
+ // Do whatever sync ops we need to do before releasing the old slot.
+ err = syncForReleaseLocked(mEglDisplay);
+ if (err != NO_ERROR) {
+ // Release the buffer we just acquired. It's not safe to
+ // release the old buffer, so instead we just drop the new frame.
+ releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR);
+ return err;
+ }
- BufferQueue::BufferItem item;
+ ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)",
+ mCurrentTexture,
+ mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
+ buf, mSlots[buf].mGraphicBuffer->handle);
- // In asynchronous mode the list is guaranteed to be one buffer
- // deep, while in synchronous mode we use the oldest buffer.
- err = acquireBufferLocked(&item);
- if (err == NO_ERROR) {
- int buf = item.mBuf;
-
- // we call the rejecter here, in case the caller has a reason to
- // not accept this buffer. this is used by SurfaceFlinger to
- // reject buffers which have the wrong size
- if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
- releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR);
- glBindTexture(mTexTarget, mTexName);
- return NO_ERROR;
+ // release old buffer
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay,
+ mEglSlots[mCurrentTexture].mEglFence);
+ if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
+ ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)",
+ strerror(-status), status);
+ err = status;
+ // keep going, with error raised [?]
}
+ }
- GLint error;
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
- }
+ // Update the GLConsumer state.
+ mCurrentTexture = buf;
+ mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+ mCurrentCrop = item.mCrop;
+ mCurrentTransform = item.mTransform;
+ mCurrentScalingMode = item.mScalingMode;
+ mCurrentTimestamp = item.mTimestamp;
+ mCurrentFence = item.mFence;
- EGLImageKHR image = mEglSlots[buf].mEglImage;
- glBindTexture(mTexTarget, mTexName);
- glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+ computeCurrentTransformMatrixLocked();
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGE("updateTexImage: error binding external texture image %p "
- "(slot %d): %#04x", image, buf, error);
- err = UNKNOWN_ERROR;
- }
+ return err;
+}
- if (err == NO_ERROR) {
- err = syncForReleaseLocked(dpy);
- }
+status_t GLConsumer::bindTextureImageLocked() {
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ ALOGE("bindTextureImage: invalid display");
+ return INVALID_OPERATION;
+ }
+ GLint error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+ }
+
+ glBindTexture(mTexTarget, mTexName);
+ if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+ if (mCurrentTextureBuf == NULL) {
+ ST_LOGE("bindTextureImage: no currently-bound texture");
+ return NO_INIT;
+ }
+ status_t err = bindUnslottedBufferLocked(mEglDisplay);
if (err != NO_ERROR) {
- // Release the buffer we just acquired. It's not safe to
- // release the old buffer, so instead we just drop the new frame.
- releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR);
return err;
}
+ } else {
+ EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage;
- ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
- mCurrentTexture,
- mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
- buf, mSlots[buf].mGraphicBuffer->handle);
-
- // release old buffer
- if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- status_t status = releaseBufferLocked(mCurrentTexture, dpy,
- mEglSlots[mCurrentTexture].mEglFence);
- if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
- ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
- strerror(-status), status);
- err = status;
- }
- }
+ glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
- // Update the SurfaceTexture state.
- mCurrentTexture = buf;
- mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
- mCurrentCrop = item.mCrop;
- mCurrentTransform = item.mTransform;
- mCurrentScalingMode = item.mScalingMode;
- mCurrentTimestamp = item.mTimestamp;
- mCurrentFence = item.mFence;
- if (!skipSync) {
- // SurfaceFlinger needs to lazily perform GLES synchronization
- // only when it's actually going to use GLES for compositing.
- // Eventually SurfaceFlinger should have its own consumer class,
- // but for now we'll just hack it in to SurfaceTexture.
- // SurfaceFlinger is responsible for calling doGLFenceWait before
- // texturing from this SurfaceTexture.
- doGLFenceWaitLocked();
- }
- computeCurrentTransformMatrixLocked();
- } else {
- if (err < 0) {
- ST_LOGE("updateTexImage: acquire failed: %s (%d)",
- strerror(-err), err);
- return err;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGE("bindTextureImage: error binding external texture image %p"
+ ": %#04x", image, error);
+ return UNKNOWN_ERROR;
}
- // We always bind the texture even if we don't update its contents.
- glBindTexture(mTexTarget, mTexName);
- return OK;
}
- return err;
+ // Wait for the new buffer to be ready.
+ return doGLFenceWaitLocked();
+
}
-void SurfaceTexture::setReleaseFence(int fenceFd) {
+status_t GLConsumer::checkAndUpdateEglStateLocked() {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) ||
+ dpy == EGL_NO_DISPLAY) {
+ ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) ||
+ ctx == EGL_NO_CONTEXT) {
+ ST_LOGE("checkAndUpdateEglState: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ return NO_ERROR;
+}
+
+void GLConsumer::setReleaseFence(int fenceFd) {
sp<Fence> fence(new Fence(fenceFd));
if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
return;
}
}
-status_t SurfaceTexture::detachFromContext() {
+status_t GLConsumer::detachFromContext() {
ATRACE_CALL();
ST_LOGV("detachFromContext");
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("detachFromContext: abandoned SurfaceTexture");
+ ST_LOGE("detachFromContext: abandoned GLConsumer");
return NO_INIT;
}
if (!mAttached) {
- ST_LOGE("detachFromContext: SurfaceTexture is not attached to a "
+ ST_LOGE("detachFromContext: GLConsumer is not attached to a "
"context");
return INVALID_OPERATION;
}
// Because we're giving up the EGLDisplay we need to free all the EGLImages
// that are associated with it. They'll be recreated when the
- // SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
+ // GLConsumer gets attached to a new OpenGL ES context (and thus gets a
// new EGLDisplay).
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
EGLImageKHR img = mEglSlots[i].mEglImage;
return OK;
}
-status_t SurfaceTexture::attachToContext(GLuint tex) {
+status_t GLConsumer::attachToContext(GLuint tex) {
ATRACE_CALL();
ST_LOGV("attachToContext");
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("attachToContext: abandoned SurfaceTexture");
+ ST_LOGE("attachToContext: abandoned GLConsumer");
return NO_INIT;
}
if (mAttached) {
- ST_LOGE("attachToContext: SurfaceTexture is already attached to a "
+ ST_LOGE("attachToContext: GLConsumer is already attached to a "
"context");
return INVALID_OPERATION;
}
if (mCurrentTextureBuf != NULL) {
// The EGLImageKHR that was associated with the slot was destroyed when
- // the SurfaceTexture was detached from the old context, so we need to
+ // the GLConsumer was detached from the old context, so we need to
// recreate it here.
- EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
- if (image == EGL_NO_IMAGE_KHR) {
- return UNKNOWN_ERROR;
- }
-
- // Attach the current buffer to the GL texture.
- glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
-
- GLint error;
- status_t err = OK;
- while ((error = glGetError()) != GL_NO_ERROR) {
- ST_LOGE("attachToContext: error binding external texture image %p "
- "(slot %d): %#04x", image, mCurrentTexture, error);
- err = UNKNOWN_ERROR;
- }
-
- // We destroy the EGLImageKHR here because the current buffer may no
- // longer be associated with one of the buffer slots, so we have
- // nowhere to to store it. If the buffer is still associated with a
- // slot then another EGLImageKHR will be created next time that buffer
- // gets acquired in updateTexImage.
- eglDestroyImageKHR(dpy, image);
-
- if (err != OK) {
+ status_t err = bindUnslottedBufferLocked(dpy);
+ if (err != NO_ERROR) {
return err;
}
}
return OK;
}
-status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
+status_t GLConsumer::bindUnslottedBufferLocked(EGLDisplay dpy) {
+ ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p",
+ mCurrentTexture, mCurrentTextureBuf.get());
+
+ // Create a temporary EGLImageKHR.
+ EGLImageKHR image = createImage(dpy, mCurrentTextureBuf);
+ if (image == EGL_NO_IMAGE_KHR) {
+ return UNKNOWN_ERROR;
+ }
+
+ // Attach the current buffer to the GL texture.
+ glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+
+ GLint error;
+ status_t err = OK;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ ST_LOGE("bindUnslottedBuffer: error binding external texture image %p "
+ "(slot %d): %#04x", image, mCurrentTexture, error);
+ err = UNKNOWN_ERROR;
+ }
+
+ // We destroy the EGLImageKHR here because the current buffer may no
+ // longer be associated with one of the buffer slots, so we have
+ // nowhere to to store it. If the buffer is still associated with a
+ // slot then another EGLImageKHR will be created next time that buffer
+ // gets acquired in updateTexImage.
+ eglDestroyImageKHR(dpy, image);
+
+ return err;
+}
+
+
+status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) {
ST_LOGV("syncForReleaseLocked");
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
- if (useNativeFenceSync) {
+ if (sUseNativeFenceSync) {
EGLSyncKHR sync = eglCreateSyncKHR(dpy,
EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
if (sync == EGL_NO_SYNC_KHR) {
return OK;
}
-bool SurfaceTexture::isExternalFormat(uint32_t format)
+bool GLConsumer::isExternalFormat(uint32_t format)
{
switch (format) {
// supported YUV formats
return false;
}
-GLenum SurfaceTexture::getCurrentTextureTarget() const {
+GLenum GLConsumer::getCurrentTextureTarget() const {
return mTexTarget;
}
-void SurfaceTexture::getTransformMatrix(float mtx[16]) {
+void GLConsumer::getTransformMatrix(float mtx[16]) {
Mutex::Autolock lock(mMutex);
memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
}
-void SurfaceTexture::setFilteringEnabled(bool enabled) {
+void GLConsumer::setFilteringEnabled(bool enabled) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
+ ST_LOGE("setFilteringEnabled: GLConsumer is abandoned!");
return;
}
bool needsRecompute = mFilteringEnabled != enabled;
}
}
-void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+void GLConsumer::computeCurrentTransformMatrixLocked() {
ST_LOGV("computeCurrentTransformMatrixLocked");
float xform[16];
mtxMul(mtxBeforeFlipV, crop, xform);
// SurfaceFlinger expects the top of its window textures to be at a Y
- // coordinate of 0, so SurfaceTexture must behave the same way. We don't
+ // coordinate of 0, so GLConsumer must behave the same way. We don't
// want to expose this to applications, however, so we must add an
// additional vertical flip to the transform after all the other transforms.
mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
}
-nsecs_t SurfaceTexture::getTimestamp() {
+nsecs_t GLConsumer::getTimestamp() {
ST_LOGV("getTimestamp");
Mutex::Autolock lock(mMutex);
return mCurrentTimestamp;
}
-EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
+EGLImageKHR GLConsumer::createImage(EGLDisplay dpy,
const sp<GraphicBuffer>& graphicBuffer) {
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
EGLint attrs[] = {
return image;
}
-sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const {
+sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
Mutex::Autolock lock(mMutex);
return mCurrentTextureBuf;
}
-Rect SurfaceTexture::getCurrentCrop() const {
+Rect GLConsumer::getCurrentCrop() const {
Mutex::Autolock lock(mMutex);
Rect outCrop = mCurrentCrop;
return outCrop;
}
-uint32_t SurfaceTexture::getCurrentTransform() const {
+uint32_t GLConsumer::getCurrentTransform() const {
Mutex::Autolock lock(mMutex);
return mCurrentTransform;
}
-uint32_t SurfaceTexture::getCurrentScalingMode() const {
+uint32_t GLConsumer::getCurrentScalingMode() const {
Mutex::Autolock lock(mMutex);
return mCurrentScalingMode;
}
-sp<Fence> SurfaceTexture::getCurrentFence() const {
+sp<Fence> GLConsumer::getCurrentFence() const {
Mutex::Autolock lock(mMutex);
return mCurrentFence;
}
-status_t SurfaceTexture::doGLFenceWait() const {
+status_t GLConsumer::doGLFenceWait() const {
Mutex::Autolock lock(mMutex);
return doGLFenceWaitLocked();
}
-status_t SurfaceTexture::doGLFenceWaitLocked() const {
+status_t GLConsumer::doGLFenceWaitLocked() const {
EGLDisplay dpy = eglGetCurrentDisplay();
EGLContext ctx = eglGetCurrentContext();
return INVALID_OPERATION;
}
- if (mCurrentFence != NULL) {
+ if (mCurrentFence->isValid()) {
if (useWaitSync) {
// Create an EGLSyncKHR from the current fence.
int fenceFd = mCurrentFence->dup();
}
} else {
status_t err = mCurrentFence->waitForever(1000,
- "SurfaceTexture::doGLFenceWaitLocked");
+ "GLConsumer::doGLFenceWaitLocked");
if (err != NO_ERROR) {
ST_LOGE("doGLFenceWait: error waiting for fence: %d", err);
return err;
return NO_ERROR;
}
-bool SurfaceTexture::isSynchronousMode() const {
+bool GLConsumer::isSynchronousMode() const {
Mutex::Autolock lock(mMutex);
return mBufferQueue->isSynchronousMode();
}
-void SurfaceTexture::freeBufferLocked(int slotIndex) {
+void GLConsumer::freeBufferLocked(int slotIndex) {
ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
if (slotIndex == mCurrentTexture) {
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
ConsumerBase::freeBufferLocked(slotIndex);
}
-void SurfaceTexture::abandonLocked() {
+void GLConsumer::abandonLocked() {
ST_LOGV("abandonLocked");
mCurrentTextureBuf.clear();
ConsumerBase::abandonLocked();
}
-void SurfaceTexture::setName(const String8& name) {
+void GLConsumer::setName(const String8& name) {
Mutex::Autolock _l(mMutex);
mName = name;
mBufferQueue->setConsumerName(name);
}
-status_t SurfaceTexture::setDefaultBufferFormat(uint32_t defaultFormat) {
+status_t GLConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
Mutex::Autolock lock(mMutex);
return mBufferQueue->setDefaultBufferFormat(defaultFormat);
}
-status_t SurfaceTexture::setConsumerUsageBits(uint32_t usage) {
+status_t GLConsumer::setConsumerUsageBits(uint32_t usage) {
Mutex::Autolock lock(mMutex);
usage |= DEFAULT_USAGE_FLAGS;
return mBufferQueue->setConsumerUsageBits(usage);
}
-status_t SurfaceTexture::setTransformHint(uint32_t hint) {
+status_t GLConsumer::setTransformHint(uint32_t hint) {
Mutex::Autolock lock(mMutex);
return mBufferQueue->setTransformHint(hint);
}
-// Used for refactoring BufferQueue from SurfaceTexture
-// Should not be in final interface once users of SurfaceTexture are clean up.
-status_t SurfaceTexture::setSynchronousMode(bool enabled) {
+// Used for refactoring BufferQueue from GLConsumer
+// Should not be in final interface once users of GLConsumer are clean up.
+status_t GLConsumer::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex);
return mBufferQueue->setSynchronousMode(enabled);
}
-void SurfaceTexture::dumpLocked(String8& result, const char* prefix,
+void GLConsumer::dumpLocked(String8& result, const char* prefix,
char* buffer, size_t size) const
{
snprintf(buffer, size,
#include <ui/GraphicBuffer.h>
-#include "DisplayHardware/GraphicBufferAlloc.h"
+#include <gui/GraphicBufferAlloc.h>
// ----------------------------------------------------------------------------
namespace android {
#include <binder/Parcel.h>
#include <binder/IInterface.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
namespace android {
// ----------------------------------------------------------------------------
};
-class BpSurfaceTexture : public BpInterface<ISurfaceTexture>
+class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
{
public:
- BpSurfaceTexture(const sp<IBinder>& impl)
- : BpInterface<ISurfaceTexture>(impl)
+ BpGraphicBufferProducer(const sp<IBinder>& impl)
+ : BpInterface<IGraphicBufferProducer>(impl)
{
}
virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
status_t result =remote()->transact(REQUEST_BUFFER, data, &reply);
if (result != NO_ERROR) {
virtual status_t setBufferCount(int bufferCount)
{
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(bufferCount);
status_t result =remote()->transact(SET_BUFFER_COUNT, data, &reply);
if (result != NO_ERROR) {
virtual status_t dequeueBuffer(int *buf, sp<Fence>& fence,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(w);
data.writeInt32(h);
data.writeInt32(format);
return result;
}
*buf = reply.readInt32();
- fence.clear();
- bool hasFence = reply.readInt32();
- if (hasFence) {
+ bool fenceWasWritten = reply.readInt32();
+ if (fenceWasWritten) {
+ // If the fence was written by the callee, then overwrite the
+ // caller's fence here. If it wasn't written then don't touch the
+ // caller's fence.
fence = new Fence();
reply.read(*fence.get());
}
virtual status_t queueBuffer(int buf,
const QueueBufferInput& input, QueueBufferOutput* output) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(buf);
data.write(input);
status_t result = remote()->transact(QUEUE_BUFFER, data, &reply);
virtual void cancelBuffer(int buf, sp<Fence> fence) {
Parcel data, reply;
- bool hasFence = fence.get() && fence->isValid();
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(buf);
- data.writeInt32(hasFence);
- if (hasFence) {
- data.write(*fence.get());
- }
+ data.write(*fence.get());
remote()->transact(CANCEL_BUFFER, data, &reply);
}
virtual int query(int what, int* value) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(what);
status_t result = remote()->transact(QUERY, data, &reply);
if (result != NO_ERROR) {
virtual status_t setSynchronousMode(bool enabled) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(enabled);
status_t result = remote()->transact(SET_SYNCHRONOUS_MODE, data, &reply);
if (result != NO_ERROR) {
virtual status_t connect(int api, QueueBufferOutput* output) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(api);
status_t result = remote()->transact(CONNECT, data, &reply);
if (result != NO_ERROR) {
virtual status_t disconnect(int api) {
Parcel data, reply;
- data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
data.writeInt32(api);
status_t result =remote()->transact(DISCONNECT, data, &reply);
if (result != NO_ERROR) {
}
};
-IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture");
+IMPLEMENT_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer");
// ----------------------------------------------------------------------
-status_t BnSurfaceTexture::onTransact(
+status_t BnGraphicBufferProducer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case REQUEST_BUFFER: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int bufferIdx = data.readInt32();
sp<GraphicBuffer> buffer;
int result = requestBuffer(bufferIdx, &buffer);
return NO_ERROR;
} break;
case SET_BUFFER_COUNT: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int bufferCount = data.readInt32();
int result = setBufferCount(bufferCount);
reply->writeInt32(result);
return NO_ERROR;
} break;
case DEQUEUE_BUFFER: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
uint32_t w = data.readInt32();
uint32_t h = data.readInt32();
uint32_t format = data.readInt32();
int buf;
sp<Fence> fence;
int result = dequeueBuffer(&buf, fence, w, h, format, usage);
- bool hasFence = fence.get() && fence->isValid();
reply->writeInt32(buf);
- reply->writeInt32(hasFence);
- if (hasFence) {
+ reply->writeInt32(fence != NULL);
+ if (fence != NULL) {
reply->write(*fence.get());
}
reply->writeInt32(result);
return NO_ERROR;
} break;
case QUEUE_BUFFER: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
QueueBufferInput input(data);
QueueBufferOutput* const output =
return NO_ERROR;
} break;
case CANCEL_BUFFER: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int buf = data.readInt32();
- sp<Fence> fence;
- bool hasFence = data.readInt32();
- if (hasFence) {
- fence = new Fence();
- data.read(*fence.get());
- }
+ sp<Fence> fence = new Fence();
+ data.read(*fence.get());
cancelBuffer(buf, fence);
return NO_ERROR;
} break;
case QUERY: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int value;
int what = data.readInt32();
int res = query(what, &value);
return NO_ERROR;
} break;
case SET_SYNCHRONOUS_MODE: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
bool enabled = data.readInt32();
status_t res = setSynchronousMode(enabled);
reply->writeInt32(res);
return NO_ERROR;
} break;
case CONNECT: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int api = data.readInt32();
QueueBufferOutput* const output =
reinterpret_cast<QueueBufferOutput *>(
return NO_ERROR;
} break;
case DISCONNECT: {
- CHECK_INTERFACE(ISurfaceTexture, data, reply);
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
int api = data.readInt32();
status_t res = disconnect(api);
reply->writeInt32(res);
// ----------------------------------------------------------------------------
-static bool isValid(const sp<Fence>& fence) {
- return fence.get() && fence->isValid();
-}
-
-ISurfaceTexture::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
+IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
parcel.read(*this);
}
-size_t ISurfaceTexture::QueueBufferInput::getFlattenedSize() const
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const
{
return sizeof(timestamp)
+ sizeof(crop)
+ sizeof(scalingMode)
+ sizeof(transform)
- + sizeof(bool)
- + (isValid(fence) ? fence->getFlattenedSize() : 0);
+ + fence->getFlattenedSize();
}
-size_t ISurfaceTexture::QueueBufferInput::getFdCount() const
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const
{
- return isValid(fence) ? fence->getFdCount() : 0;
+ return fence->getFdCount();
}
-status_t ISurfaceTexture::QueueBufferInput::flatten(void* buffer, size_t size,
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(void* buffer, size_t size,
int fds[], size_t count) const
{
status_t err = NO_ERROR;
- bool haveFence = isValid(fence);
char* p = (char*)buffer;
memcpy(p, ×tamp, sizeof(timestamp)); p += sizeof(timestamp);
memcpy(p, &crop, sizeof(crop)); p += sizeof(crop);
memcpy(p, &scalingMode, sizeof(scalingMode)); p += sizeof(scalingMode);
memcpy(p, &transform, sizeof(transform)); p += sizeof(transform);
- memcpy(p, &haveFence, sizeof(haveFence)); p += sizeof(haveFence);
- if (haveFence) {
- err = fence->flatten(p, size - (p - (char*)buffer), fds, count);
- }
+ err = fence->flatten(p, size - (p - (char*)buffer), fds, count);
return err;
}
-status_t ISurfaceTexture::QueueBufferInput::unflatten(void const* buffer,
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(void const* buffer,
size_t size, int fds[], size_t count)
{
status_t err = NO_ERROR;
- bool haveFence;
const char* p = (const char*)buffer;
memcpy(×tamp, p, sizeof(timestamp)); p += sizeof(timestamp);
memcpy(&crop, p, sizeof(crop)); p += sizeof(crop);
memcpy(&scalingMode, p, sizeof(scalingMode)); p += sizeof(scalingMode);
memcpy(&transform, p, sizeof(transform)); p += sizeof(transform);
- memcpy(&haveFence, p, sizeof(haveFence)); p += sizeof(haveFence);
- if (haveFence) {
- fence = new Fence();
- err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count);
- }
+ fence = new Fence();
+ err = fence->unflatten(p, size - (p - (const char*)buffer), fds, count);
return err;
}
#include <binder/Parcel.h>
#include <gui/ISurface.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
namespace android {
{
}
- virtual sp<ISurfaceTexture> getSurfaceTexture() const {
+ virtual sp<IGraphicBufferProducer> getSurfaceTexture() const {
Parcel data, reply;
data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
remote()->transact(GET_SURFACE_TEXTURE, data, &reply);
- return interface_cast<ISurfaceTexture>(reply.readStrongBinder());
+ return interface_cast<IGraphicBufferProducer>(reply.readStrongBinder());
}
};
#include <gui/BitTube.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <private/gui/LayerState.h>
}
virtual bool authenticateSurfaceTexture(
- const sp<ISurfaceTexture>& surfaceTexture) const
+ const sp<IGraphicBufferProducer>& bufferProducer) const
{
Parcel data, reply;
int err = NO_ERROR;
"interface descriptor: %s (%d)", strerror(-err), -err);
return false;
}
- err = data.writeStrongBinder(surfaceTexture->asBinder());
+ err = data.writeStrongBinder(bufferProducer->asBinder());
if (err != NO_ERROR) {
ALOGE("ISurfaceComposer::authenticateSurfaceTexture: error writing "
"strong binder to parcel: %s (%d)", strerror(-err), -err);
} break;
case AUTHENTICATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<ISurfaceTexture> surfaceTexture =
- interface_cast<ISurfaceTexture>(data.readStrongBinder());
- int32_t result = authenticateSurfaceTexture(surfaceTexture) ? 1 : 0;
+ sp<IGraphicBufferProducer> bufferProducer =
+ interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+ int32_t result = authenticateSurfaceTexture(bufferProducer) ? 1 : 0;
reply->writeInt32(result);
} break;
case CREATE_DISPLAY_EVENT_CONNECTION: {
#include <utils/Errors.h>
#include <binder/Parcel.h>
#include <gui/ISurfaceComposerClient.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
#include <private/gui/LayerState.h>
namespace android {
status_t DisplayState::read(const Parcel& input) {
token = input.readStrongBinder();
- surface = interface_cast<ISurfaceTexture>(input.readStrongBinder());
+ surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
what = input.readInt32();
layerStack = input.readInt32();
orientation = input.readInt32();
identity = control->mIdentity;
}
parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
- parcel->writeStrongBinder(NULL); // NULL ISurfaceTexture in this case.
+ parcel->writeStrongBinder(NULL); // NULL IGraphicBufferProducer in this case.
parcel->writeInt32(identity);
return NO_ERROR;
}
mSurface(surface->mSurface),
mIdentity(surface->mIdentity)
{
- sp<ISurfaceTexture> st;
+ sp<IGraphicBufferProducer> st;
if (mSurface != NULL) {
st = mSurface->getSurfaceTexture();
}
{
mSurface = interface_cast<ISurface>(ref);
sp<IBinder> st_binder(parcel.readStrongBinder());
- sp<ISurfaceTexture> st;
+ sp<IGraphicBufferProducer> st;
if (st_binder != NULL) {
- st = interface_cast<ISurfaceTexture>(st_binder);
+ st = interface_cast<IGraphicBufferProducer>(st_binder);
} else if (mSurface != NULL) {
st = mSurface->getSurfaceTexture();
}
init(st);
}
-Surface::Surface(const sp<ISurfaceTexture>& st)
+Surface::Surface(const sp<IGraphicBufferProducer>& st)
: SurfaceTextureClient(),
mSurface(NULL),
mIdentity(0)
const sp<Surface>& surface, Parcel* parcel)
{
sp<ISurface> sur;
- sp<ISurfaceTexture> st;
+ sp<IGraphicBufferProducer> st;
uint32_t identity = 0;
if (Surface::isValid(surface)) {
sur = surface->mSurface;
} else if (surface != 0 &&
(surface->mSurface != NULL ||
surface->getISurfaceTexture() != NULL)) {
- ALOGE("Parceling invalid surface with non-NULL ISurface/ISurfaceTexture as NULL: "
- "mSurface = %p, surfaceTexture = %p, mIdentity = %d, ",
+ ALOGE("Parceling invalid surface with non-NULL ISurface/IGraphicBufferProducer "
+ "as NULL: mSurface = %p, bufferProducer = %p, mIdentity = %d, ",
surface->mSurface.get(), surface->getISurfaceTexture().get(),
surface->mIdentity);
}
} else {
// The Surface was found in the cache, but we still should clear any
// remaining data from the parcel.
- data.readStrongBinder(); // ISurfaceTexture
+ data.readStrongBinder(); // IGraphicBufferProducer
data.readInt32(); // identity
}
if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
}
}
-void Surface::init(const sp<ISurfaceTexture>& surfaceTexture)
+void Surface::init(const sp<IGraphicBufferProducer>& bufferProducer)
{
- if (mSurface != NULL || surfaceTexture != NULL) {
- ALOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface");
- if (surfaceTexture != NULL) {
- setISurfaceTexture(surfaceTexture);
+ if (mSurface != NULL || bufferProducer != NULL) {
+ ALOGE_IF(bufferProducer==0, "got a NULL IGraphicBufferProducer from ISurface");
+ if (bufferProducer != NULL) {
+ setISurfaceTexture(bufferProducer);
setUsage(GraphicBuffer::USAGE_HW_RENDER);
}
return getISurfaceTexture() != NULL;
}
-sp<ISurfaceTexture> Surface::getSurfaceTexture() {
+sp<IGraphicBufferProducer> Surface::getSurfaceTexture() {
return getISurfaceTexture();
}
SortedVector<ComposerState> mComposerStates;
SortedVector<DisplayState > mDisplayStates;
uint32_t mForceSynchronous;
+ uint32_t mTransactionNestCount;
bool mAnimation;
Composer() : Singleton<Composer>(),
- mForceSynchronous(0),
+ mForceSynchronous(0), mTransactionNestCount(0),
mAnimation(false)
{ }
+ void openGlobalTransactionImpl();
void closeGlobalTransactionImpl(bool synchronous);
void setAnimationTransactionImpl();
status_t setLayerStack(const sp<SurfaceComposerClient>& client,
SurfaceID id, uint32_t layerStack);
- void setDisplaySurface(const sp<IBinder>& token, const sp<ISurfaceTexture>& surface);
+ void setDisplaySurface(const sp<IBinder>& token,
+ const sp<IGraphicBufferProducer>& bufferProducer);
void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
void setDisplayProjection(const sp<IBinder>& token,
uint32_t orientation,
Composer::getInstance().setAnimationTransactionImpl();
}
+ static void openGlobalTransaction() {
+ Composer::getInstance().openGlobalTransactionImpl();
+ }
+
static void closeGlobalTransaction(bool synchronous) {
Composer::getInstance().closeGlobalTransactionImpl(synchronous);
}
return ComposerService::getComposerService()->getBuiltInDisplay(id);
}
+void Composer::openGlobalTransactionImpl() {
+ { // scope for the lock
+ Mutex::Autolock _l(mLock);
+ mTransactionNestCount += 1;
+ }
+}
+
void Composer::closeGlobalTransactionImpl(bool synchronous) {
sp<ISurfaceComposer> sm(ComposerService::getComposerService());
{ // scope for the lock
Mutex::Autolock _l(mLock);
+ mForceSynchronous |= synchronous;
+ if (!mTransactionNestCount) {
+ ALOGW("At least one call to closeGlobalTransaction() was not matched by a prior "
+ "call to openGlobalTransaction().");
+ } else if (--mTransactionNestCount) {
+ return;
+ }
+
transaction = mComposerStates;
mComposerStates.clear();
displayTransaction = mDisplayStates;
mDisplayStates.clear();
- if (synchronous || mForceSynchronous) {
+ if (mForceSynchronous) {
flags |= ISurfaceComposer::eSynchronous;
}
if (mAnimation) {
}
void Composer::setDisplaySurface(const sp<IBinder>& token,
- const sp<ISurfaceTexture>& surface) {
+ const sp<IGraphicBufferProducer>& bufferProducer) {
Mutex::Autolock _l(mLock);
DisplayState& s(getDisplayStateLocked(token));
- s.surface = surface;
+ s.surface = bufferProducer;
s.what |= DisplayState::eSurfaceChanged;
}
// ----------------------------------------------------------------------------
void SurfaceComposerClient::openGlobalTransaction() {
- // Currently a no-op
+ Composer::openGlobalTransaction();
}
void SurfaceComposerClient::closeGlobalTransaction(bool synchronous) {
// ----------------------------------------------------------------------------
void SurfaceComposerClient::setDisplaySurface(const sp<IBinder>& token,
- const sp<ISurfaceTexture>& surface) {
- Composer::getInstance().setDisplaySurface(token, surface);
+ const sp<IGraphicBufferProducer>& bufferProducer) {
+ Composer::getInstance().setDisplaySurface(token, bufferProducer);
}
void SurfaceComposerClient::setDisplayLayerStack(const sp<IBinder>& token,
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceTexture.h>
+#include <gui/GLConsumer.h>
#include <gui/SurfaceTextureClient.h>
#include <private/gui/ComposerService.h>
namespace android {
SurfaceTextureClient::SurfaceTextureClient(
- const sp<ISurfaceTexture>& surfaceTexture)
+ const sp<IGraphicBufferProducer>& bufferProducer)
{
SurfaceTextureClient::init();
- SurfaceTextureClient::setISurfaceTexture(surfaceTexture);
-}
-
-// see SurfaceTextureClient.h
-SurfaceTextureClient::SurfaceTextureClient(const
- sp<SurfaceTexture>& surfaceTexture)
-{
- SurfaceTextureClient::init();
- SurfaceTextureClient::setISurfaceTexture(surfaceTexture->getBufferQueue());
+ SurfaceTextureClient::setISurfaceTexture(bufferProducer);
}
SurfaceTextureClient::SurfaceTextureClient() {
}
void SurfaceTextureClient::setISurfaceTexture(
- const sp<ISurfaceTexture>& surfaceTexture)
+ const sp<IGraphicBufferProducer>& bufferProducer)
{
- mSurfaceTexture = surfaceTexture;
+ mSurfaceTexture = bufferProducer;
}
-sp<ISurfaceTexture> SurfaceTextureClient::getISurfaceTexture() const {
+sp<IGraphicBufferProducer> SurfaceTextureClient::getISurfaceTexture() const {
return mSurfaceTexture;
}
status_t result = mSurfaceTexture->dequeueBuffer(&buf, fence, reqW, reqH,
mReqFormat, mReqUsage);
if (result < 0) {
- ALOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer(%d, %d, %d, %d)"
+ ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d)"
"failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage,
result);
return result;
}
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
- if (result & ISurfaceTexture::RELEASE_ALL_BUFFERS) {
+ if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
- if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
+ if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
result = mSurfaceTexture->requestBuffer(buf, &gbuf);
if (result != NO_ERROR) {
- ALOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed: %d",
+ ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d",
result);
return result;
}
}
- if (fence.get()) {
+ if (fence->isValid()) {
*fenceFd = fence->dup();
if (*fenceFd == -1) {
ALOGE("dequeueBuffer: error duping fence: %d", errno);
if (i < 0) {
return i;
}
- sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : NULL);
+ sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
mSurfaceTexture->cancelBuffer(i, fence);
return OK;
}
Rect crop;
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
- sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : NULL);
- ISurfaceTexture::QueueBufferOutput output;
- ISurfaceTexture::QueueBufferInput input(timestamp, crop, mScalingMode,
+ sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
+ IGraphicBufferProducer::QueueBufferOutput output;
+ IGraphicBufferProducer::QueueBufferInput input(timestamp, crop, mScalingMode,
mTransform, fence);
status_t err = mSurfaceTexture->queueBuffer(i, input, &output);
if (err != OK) {
ATRACE_CALL();
ALOGV("SurfaceTextureClient::connect");
Mutex::Autolock lock(mMutex);
- ISurfaceTexture::QueueBufferOutput output;
+ IGraphicBufferProducer::QueueBufferOutput output;
int err = mSurfaceTexture->connect(api, &output);
if (err == NO_ERROR) {
uint32_t numPendingBuffers = 0;
Mutex::Autolock lock(mMutex);
status_t err = mSurfaceTexture->setBufferCount(bufferCount);
- ALOGE_IF(err, "ISurfaceTexture::setBufferCount(%d) returned %s",
+ ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s",
bufferCount, strerror(-err));
if (err == NO_ERROR) {
TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
sp<DummyConsumer> dc(new DummyConsumer);
mBQ->consumerConnect(dc);
- ISurfaceTexture::QueueBufferOutput qbo;
+ IGraphicBufferProducer::QueueBufferOutput qbo;
mBQ->connect(NATIVE_WINDOW_API_CPU, &qbo);
mBQ->setBufferCount(4);
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
- ISurfaceTexture::QueueBufferInput qbi(0, Rect(0, 0, 1, 1),
- NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, fence);
+ IGraphicBufferProducer::QueueBufferInput qbi(0, Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
BufferQueue::BufferItem item;
for (int i = 0; i < 2; i++) {
- ASSERT_EQ(ISurfaceTexture::BUFFER_NEEDS_REALLOCATION,
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mBQ->dequeueBuffer(&slot, fence, 1, 1, 0,
GRALLOC_USAGE_SW_READ_OFTEN));
ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf));
ASSERT_EQ(OK, mBQ->acquireBuffer(&item));
}
- ASSERT_EQ(ISurfaceTexture::BUFFER_NEEDS_REALLOCATION,
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
mBQ->dequeueBuffer(&slot, fence, 1, 1, 0,
GRALLOC_USAGE_SW_READ_OFTEN));
ASSERT_EQ(OK, mBQ->requestBuffer(slot, &buf));
ALOGV("Begin test: %s.%s", testInfo->test_case_name(),
testInfo->name());
- mST = new SurfaceTexture(123);
- mSTC = new SurfaceTextureClient(mST);
+ mST = new GLConsumer(123);
+ mSTC = new SurfaceTextureClient(mST->getBufferQueue());
mANW = mSTC;
// We need a valid GL context so we can test updateTexImage()
return sDefaultConfigAttribs;
}
- sp<SurfaceTexture> mST;
+ sp<GLConsumer> mST;
sp<SurfaceTextureClient> mSTC;
sp<ANativeWindow> mANW;
};
TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
- sp<ISurfaceTexture> ist(mSTC->getISurfaceTexture());
+ sp<IGraphicBufferProducer> ist(mSTC->getISurfaceTexture());
ASSERT_TRUE(ist != NULL);
}
}
TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) {
- sp<SurfaceTexture> st(mST);
+ sp<GLConsumer> st(mST);
ANativeWindowBuffer* buf;
EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
ASSERT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &buf));
// from the SurfaceTexture class.
TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
class MyThread : public Thread {
- sp<SurfaceTexture> mST;
+ sp<GLConsumer> mST;
EGLContext ctx;
EGLSurface sur;
EGLDisplay dpy;
return false;
}
public:
- MyThread(const sp<SurfaceTexture>& mST)
+ MyThread(const sp<GLConsumer>& mST)
: mST(mST), mBufferRetired(false) {
ctx = eglGetCurrentContext();
sur = eglGetCurrentSurface(EGL_DRAW);
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
- sp<SurfaceTexture> st(new SurfaceTexture(i));
- sp<SurfaceTextureClient> stc(new SurfaceTextureClient(st));
+ sp<GLConsumer> st(new GLConsumer(i));
+ sp<SurfaceTextureClient> stc(new SurfaceTextureClient(st->getBufferQueue()));
mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
static_cast<ANativeWindow*>(stc.get()), NULL);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
//#define LOG_NDEBUG 0
#include <gtest/gtest.h>
-#include <gui/SurfaceTexture.h>
+#include <gui/GLConsumer.h>
#include <gui/SurfaceTextureClient.h>
#include <ui/GraphicBuffer.h>
#include <utils/String8.h>
while ((err = glGetError()) != GL_NO_ERROR) {
msg += String8::format(", %#x", err);
}
- fprintf(stderr, "pixel check failure: %s\n", msg.string());
return ::testing::AssertionFailure(
::testing::Message(msg.string()));
}
msg += String8::format("a(%d isn't %d)", pixel[3], a);
}
if (!msg.isEmpty()) {
- fprintf(stderr, "pixel check failure: %s\n", msg.string());
return ::testing::AssertionFailure(
::testing::Message(msg.string()));
} else {
virtual void SetUp() {
GLTest::SetUp();
- mST = new SurfaceTexture(TEX_ID);
- mSTC = new SurfaceTextureClient(mST);
+ mST = new GLConsumer(TEX_ID);
+ mSTC = new SurfaceTextureClient(mST->getBufferQueue());
mANW = mSTC;
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp());
class TextureRenderer: public RefBase {
public:
- TextureRenderer(GLuint texName, const sp<SurfaceTexture>& st):
+ TextureRenderer(GLuint texName, const sp<GLConsumer>& st):
mTexName(texName),
mST(st) {
}
ASSERT_NE(-1, mTexMatrixHandle);
}
- // drawTexture draws the SurfaceTexture over the entire GL viewport.
+ // drawTexture draws the GLConsumer over the entire GL viewport.
void drawTexture() {
static const GLfloat triangleVertices[] = {
-1.0f, 1.0f,
}
GLuint mTexName;
- sp<SurfaceTexture> mST;
+ sp<GLConsumer> mST;
GLuint mPgm;
GLint mPositionHandle;
GLint mTexSamplerHandle;
GLint mTexMatrixHandle;
};
- class FrameWaiter : public SurfaceTexture::FrameAvailableListener {
+ class FrameWaiter : public GLConsumer::FrameAvailableListener {
public:
FrameWaiter():
mPendingFrames(0) {
Condition mCondition;
};
- // Note that SurfaceTexture will lose the notifications
+ // Note that GLConsumer will lose the notifications
// onBuffersReleased and onFrameAvailable as there is currently
// no way to forward the events. This DisconnectWaiter will not let the
// disconnect finish until finishDisconnect() is called. It will
Condition mFrameCondition;
};
- sp<SurfaceTexture> mST;
+ sp<GLConsumer> mST;
sp<SurfaceTextureClient> mSTC;
sp<ANativeWindow> mANW;
sp<TextureRenderer> mTextureRenderer;
EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35));
}
-// Tests if SurfaceTexture and BufferQueue are robust enough
+// Tests if GLConsumer and BufferQueue are robust enough
// to handle a special case where updateTexImage is called
// in the middle of disconnect. This ordering is enforced
// by blocking in the disconnect callback.
sp<Thread> pt(new ProducerThread(mANW));
pt->run();
- // eat a frame so SurfaceTexture will own an at least one slot
+ // eat a frame so GLConsumer will own an at least one slot
dw->waitForFrame();
EXPECT_EQ(OK,mST->updateTexImage());
dw->waitForFrame();
- // Could fail here as SurfaceTexture thinks it still owns the slot
+ // Could fail here as GLConsumer thinks it still owns the slot
// but bufferQueue has released all slots
EXPECT_EQ(OK,mST->updateTexImage());
}
-// This test ensures that the SurfaceTexture clears the mCurrentTexture
+// This test ensures that the GLConsumer clears the mCurrentTexture
// when it is disconnected and reconnected. Otherwise it will
// attempt to release a buffer that it does not owned
TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) {
// This test should have the only reference to buffer 0.
EXPECT_EQ(1, buffers[0]->getStrongCount());
- // The SurfaceTexture should hold a single reference to buffer 1 in its
+ // The GLConsumer should hold a single reference to buffer 1 in its
// mCurrentBuffer member. All of the references in the slots should have
// been released.
EXPECT_EQ(2, buffers[1]->getStrongCount());
buffers[i] = mST->getCurrentBuffer();
}
- // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
+ // Abandon the GLConsumer, releasing the ref that the GLConsumer has
// on buffers[2].
mST->abandon();
* This test fixture is for testing GL -> GL texture streaming from one thread
* to another. It contains functionality to create a producer thread that will
* perform GL rendering to an ANativeWindow that feeds frames to a
- * SurfaceTexture. Additionally it supports interlocking the producer and
+ * GLConsumer. Additionally it supports interlocking the producer and
* consumer threads so that a specific sequence of calls can be
* deterministically created by the test.
*
// FrameCondition is a utility class for interlocking between the producer
// and consumer threads. The FrameCondition object should be created and
// destroyed in the consumer thread only. The consumer thread should set
- // the FrameCondition as the FrameAvailableListener of the SurfaceTexture,
+ // the FrameCondition as the FrameAvailableListener of the GLConsumer,
// and should call both waitForFrame and finishFrame once for each expected
// frame.
//
// This interlocking relies on the fact that onFrameAvailable gets called
- // synchronously from SurfaceTexture::queueBuffer.
- class FrameCondition : public SurfaceTexture::FrameAvailableListener {
+ // synchronously from GLConsumer::queueBuffer.
+ class FrameCondition : public GLConsumer::FrameAvailableListener {
public:
FrameCondition():
mFrameAvailable(false),
ALOGV("-finishFrame");
}
- // This should be called by SurfaceTexture on the producer thread.
+ // This should be called by GLConsumer on the producer thread.
virtual void onFrameAvailable() {
Mutex::Autolock lock(mMutex);
ALOGV("+onFrameAvailable");
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
+ // This is needed for stdint.h to define INT64_MAX in C++
+ #define __STDC_LIMIT_MACROS
+
#include <sync/sync.h>
#include <ui/Fence.h>
#include <unistd.h>
namespace android {
-const sp<Fence> Fence::NO_FENCE = sp<Fence>();
+const sp<Fence> Fence::NO_FENCE = sp<Fence>(new Fence);
Fence::Fence() :
mFenceFd(-1) {
sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
const sp<Fence>& f2) {
ATRACE_CALL();
- int result = sync_merge(name.string(), f1->mFenceFd, f2->mFenceFd);
+ int result;
+ // Merge the two fences. In the case where one of the fences is not a
+ // valid fence (e.g. NO_FENCE) we merge the one valid fence with itself so
+ // that a new fence with the given name is created.
+ if (f1->isValid() && f2->isValid()) {
+ result = sync_merge(name.string(), f1->mFenceFd, f2->mFenceFd);
+ } else if (f1->isValid()) {
+ result = sync_merge(name.string(), f1->mFenceFd, f1->mFenceFd);
+ } else if (f2->isValid()) {
+ result = sync_merge(name.string(), f2->mFenceFd, f2->mFenceFd);
+ } else {
+ return NO_FENCE;
+ }
if (result == -1) {
status_t err = -errno;
ALOGE("merge: sync_merge(\"%s\", %d, %d) returned an error: %s (%d)",
}
int Fence::dup() const {
+ return ::dup(mFenceFd);
+}
+
+nsecs_t Fence::getSignalTime() const {
if (mFenceFd == -1) {
return -1;
}
- return ::dup(mFenceFd);
+
+ struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd);
+ if (finfo == NULL) {
+ ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd);
+ return -1;
+ }
+ if (finfo->status != 1) {
+ sync_fence_info_free(finfo);
+ return INT64_MAX;
+ }
+
+ struct sync_pt_info* pinfo = NULL;
+ uint64_t timestamp = 0;
+ while ((pinfo = sync_pt_info(finfo, pinfo)) != NULL) {
+ if (pinfo->timestamp_ns > timestamp) {
+ timestamp = pinfo->timestamp_ns;
+ }
+ }
+ sync_fence_info_free(finfo);
+
+ return nsecs_t(timestamp);
}
size_t Fence::getFlattenedSize() const {
}
size_t Fence::getFdCount() const {
- return 1;
+ return isValid() ? 1 : 0;
}
status_t Fence::flatten(void* buffer, size_t size, int fds[],
size_t count) const {
- if (size != 0 || count != 1) {
+ if (size != getFlattenedSize() || count != getFdCount()) {
return BAD_VALUE;
}
- fds[0] = mFenceFd;
+ if (isValid()) {
+ fds[0] = mFenceFd;
+ }
return NO_ERROR;
}
status_t Fence::unflatten(void const* buffer, size_t size, int fds[],
size_t count) {
- if (size != 0 || count != 1) {
+ if (size != 0 || (count != 0 && count != 1)) {
return BAD_VALUE;
}
if (mFenceFd != -1) {
return INVALID_OPERATION;
}
- mFenceFd = fds[0];
+ if (count == 1) {
+ mFenceFd = fds[0];
+ }
+
return NO_ERROR;
}
ALOGD("%s", s.string());
}
-class BufferLiberatorThread : public Thread {
-public:
-
- static void queueCaptiveBuffer(buffer_handle_t handle) {
- size_t queueSize;
- {
- Mutex::Autolock lock(sMutex);
- if (sThread == NULL) {
- sThread = new BufferLiberatorThread;
- sThread->run("BufferLiberator");
- }
-
- sThread->mQueue.push_back(handle);
- sThread->mQueuedCondition.signal();
- queueSize = sThread->mQueue.size();
- }
- }
-
- static void waitForLiberation() {
- Mutex::Autolock lock(sMutex);
-
- waitForLiberationLocked();
- }
-
- static void maybeWaitForLiberation() {
- Mutex::Autolock lock(sMutex);
- if (sThread != NULL) {
- if (sThread->mQueue.size() > 8) {
- waitForLiberationLocked();
- }
- }
- }
-
-private:
-
- BufferLiberatorThread() {}
-
- virtual bool threadLoop() {
- buffer_handle_t handle;
- { // Scope for mutex
- Mutex::Autolock lock(sMutex);
- while (mQueue.isEmpty()) {
- mQueuedCondition.wait(sMutex);
- }
- handle = mQueue[0];
- }
-
- status_t err;
- GraphicBufferAllocator& gba(GraphicBufferAllocator::get());
- { // Scope for tracing
- ATRACE_NAME("gralloc::free");
- err = gba.mAllocDev->free(gba.mAllocDev, handle);
- }
- ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err));
-
- if (err == NO_ERROR) {
- Mutex::Autolock _l(GraphicBufferAllocator::sLock);
- KeyedVector<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t>&
- list(GraphicBufferAllocator::sAllocList);
- list.removeItem(handle);
- }
-
- { // Scope for mutex
- Mutex::Autolock lock(sMutex);
- mQueue.removeAt(0);
- mFreedCondition.broadcast();
- }
-
- return true;
- }
-
- static void waitForLiberationLocked() {
- if (sThread == NULL) {
- return;
- }
-
- const nsecs_t timeout = 500 * 1000 * 1000;
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t timeToStop = now + timeout;
- while (!sThread->mQueue.isEmpty() && now < timeToStop) {
- sThread->mFreedCondition.waitRelative(sMutex, timeToStop - now);
- now = systemTime(SYSTEM_TIME_MONOTONIC);
- }
-
- if (!sThread->mQueue.isEmpty()) {
- ALOGW("waitForLiberationLocked timed out");
- }
- }
-
- static Mutex sMutex;
- static sp<BufferLiberatorThread> sThread;
- Vector<buffer_handle_t> mQueue;
- Condition mQueuedCondition;
- Condition mFreedCondition;
-};
-
-Mutex BufferLiberatorThread::sMutex;
-sp<BufferLiberatorThread> BufferLiberatorThread::sThread;
-
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
int usage, buffer_handle_t* handle, int32_t* stride)
{
w = h = 1;
// we have a h/w allocator and h/w buffer is requested
- status_t err;
-
- // If too many async frees are queued up then wait for some of them to
- // complete before attempting to allocate more memory. This is exercised
- // by the android.opengl.cts.GLSurfaceViewTest CTS test.
- BufferLiberatorThread::maybeWaitForLiberation();
-
+ status_t err;
+
err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
- if (err != NO_ERROR) {
- ALOGW("WOW! gralloc alloc failed, waiting for pending frees!");
- BufferLiberatorThread::waitForLiberation();
- err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
- }
-
ALOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)",
w, h, format, usage, err, strerror(-err));
-
+
if (err == NO_ERROR) {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
return err;
}
-
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
{
- BufferLiberatorThread::queueCaptiveBuffer(handle);
- return NO_ERROR;
+ ATRACE_CALL();
+ status_t err;
+
+ err = mAllocDev->free(mAllocDev, handle);
+
+ ALOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err));
+ if (err == NO_ERROR) {
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ list.removeItem(handle);
+ }
+
+ return err;
}
// ---------------------------------------------------------------------------
Debug.cpp \
FileMap.cpp \
Flattenable.cpp \
+ JenkinsHash.cpp \
+ LinearAllocator.cpp \
LinearTransform.cpp \
Log.cpp \
PropertyMap.cpp \
LOCAL_LDLIBS += -lrt -ldl
endif
+ifeq ($(TARGET_ARCH),mips)
+LOCAL_CFLAGS += -DALIGN_DOUBLE
+endif
+
LOCAL_C_INCLUDES += \
bionic/libc/private \
external/zlib
SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets);
if (sb->onlyOwner()) {
destroyBuckets(mBuckets, mBucketCount);
- for (size_t i = 0; i < mSize; i++) {
+ for (size_t i = 0; i < mBucketCount; i++) {
Bucket& bucket = bucketAt(mBuckets, i);
bucket.cookie = 0;
}
char line[MAX_BACKTRACE_LINE_LENGTH];
format_backtrace_line(i, &mStack[i], &symbols[i],
line, MAX_BACKTRACE_LINE_LENGTH);
- str.append(prefix);
+ if (prefix) {
+ str.append(prefix);
+ }
str.append(line);
str.append("\n");
}
--- /dev/null
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/* Implementation of Jenkins one-at-a-time hash function. These choices are
+ * optimized for code size and portability, rather than raw speed. But speed
+ * should still be quite good.
+ **/
+
+#include <utils/JenkinsHash.h>
+
+namespace android {
+
+hash_t JenkinsHashWhiten(uint32_t hash) {
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {
+ hash = JenkinsHashMix(hash, (uint32_t)size);
+ size_t i;
+ for (i = 0; i < (size & -4); i += 4) {
+ uint32_t data = bytes[i] | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);
+ hash = JenkinsHashMix(hash, data);
+ }
+ if (size & 3) {
+ uint32_t data = bytes[i];
+ data |= ((size & 3) > 1) ? (bytes[i+1] << 8) : 0;
+ data |= ((size & 3) > 2) ? (bytes[i+2] << 16) : 0;
+ hash = JenkinsHashMix(hash, data);
+ }
+ return hash;
+}
+
+uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) {
+ hash = JenkinsHashMix(hash, (uint32_t)size);
+ size_t i;
+ for (i = 0; i < (size & -2); i += 2) {
+ uint32_t data = shorts[i] | (shorts[i+1] << 16);
+ hash = JenkinsHashMix(hash, data);
+ }
+ if (size & 1) {
+ uint32_t data = shorts[i];
+ hash = JenkinsHashMix(hash, data);
+ }
+ return hash;
+}
+
+}
+
--- /dev/null
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "LinearAllocator"
+#define LOG_NDEBUG 1
+
+#include <stdlib.h>
+#include <utils/LinearAllocator.h>
+#include <utils/Log.h>
+
+
+// The ideal size of a page allocation (these need to be multiples of 8)
+#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb
+#define MAX_PAGE_SIZE ((size_t)131072) // 128kb
+
+// The maximum amount of wasted space we can have per page
+// Allocations exceeding this will have their own dedicated page
+// If this is too low, we will malloc too much
+// Too high, and we may waste too much space
+// Must be smaller than INITIAL_PAGE_SIZE
+#define MAX_WASTE_SIZE ((size_t)1024)
+
+#if ALIGN_DOUBLE
+#define ALIGN_SZ (sizeof(double))
+#else
+#define ALIGN_SZ (sizeof(int))
+#endif
+
+#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1))
+#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p)))
+
+#if LOG_NDEBUG
+#define ADD_ALLOCATION(size)
+#define RM_ALLOCATION(size)
+#else
+#include <utils/Thread.h>
+#include <utils/Timers.h>
+static size_t s_totalAllocations = 0;
+static nsecs_t s_nextLog = 0;
+static android::Mutex s_mutex;
+
+static void _logUsageLocked() {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now > s_nextLog) {
+ s_nextLog = now + milliseconds_to_nanoseconds(10);
+ ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024);
+ }
+}
+
+static void _addAllocation(size_t size) {
+ android::AutoMutex lock(s_mutex);
+ s_totalAllocations += size;
+ _logUsageLocked();
+}
+
+#define ADD_ALLOCATION(size) _addAllocation(size);
+#define RM_ALLOCATION(size) _addAllocation(-size);
+#endif
+
+#define min(x,y) (((x) < (y)) ? (x) : (y))
+
+namespace android {
+
+class LinearAllocator::Page {
+public:
+ Page* next() { return mNextPage; }
+ void setNext(Page* next) { mNextPage = next; }
+
+ Page()
+ : mNextPage(0)
+ {}
+
+ void* operator new(size_t size, void* buf) { return buf; }
+
+ void* start() {
+ return (void*) (((size_t)this) + sizeof(Page));
+ }
+
+ void* end(int pageSize) {
+ return (void*) (((size_t)start()) + pageSize);
+ }
+
+private:
+ Page(const Page& other) {}
+ Page* mNextPage;
+};
+
+LinearAllocator::LinearAllocator()
+ : mPageSize(INITIAL_PAGE_SIZE)
+ , mMaxAllocSize(MAX_WASTE_SIZE)
+ , mNext(0)
+ , mCurrentPage(0)
+ , mPages(0)
+ , mTotalAllocated(0)
+ , mWastedSpace(0)
+ , mPageCount(0)
+ , mDedicatedPageCount(0) {}
+
+LinearAllocator::~LinearAllocator(void) {
+ Page* p = mPages;
+ while (p) {
+ Page* next = p->next();
+ p->~Page();
+ free(p);
+ RM_ALLOCATION(mPageSize);
+ p = next;
+ }
+}
+
+void* LinearAllocator::start(Page* p) {
+ return ALIGN_PTR(((size_t*)p) + sizeof(Page));
+}
+
+void* LinearAllocator::end(Page* p) {
+ return ((char*)p) + mPageSize;
+}
+
+bool LinearAllocator::fitsInCurrentPage(size_t size) {
+ return mNext && ((char*)mNext + size) <= end(mCurrentPage);
+}
+
+void LinearAllocator::ensureNext(size_t size) {
+ if (fitsInCurrentPage(size)) return;
+
+ if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) {
+ mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2);
+ mPageSize = ALIGN(mPageSize);
+ }
+ mWastedSpace += mPageSize;
+ Page* p = newPage(mPageSize);
+ if (mCurrentPage) {
+ mCurrentPage->setNext(p);
+ }
+ mCurrentPage = p;
+ if (!mPages) {
+ mPages = mCurrentPage;
+ }
+ mNext = start(mCurrentPage);
+}
+
+void* LinearAllocator::alloc(size_t size) {
+ size = ALIGN(size);
+ if (size > mMaxAllocSize && !fitsInCurrentPage(size)) {
+ ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize);
+ // Allocation is too large, create a dedicated page for the allocation
+ Page* page = newPage(size);
+ mDedicatedPageCount++;
+ page->setNext(mPages);
+ mPages = page;
+ if (!mCurrentPage)
+ mCurrentPage = mPages;
+ return start(page);
+ }
+ ensureNext(size);
+ void* ptr = mNext;
+ mNext = ((char*)mNext) + size;
+ mWastedSpace -= size;
+ return ptr;
+}
+
+void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) {
+ // Don't bother rewinding across pages
+ allocSize = ALIGN(allocSize);
+ if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage)
+ && ptr == ((char*)mNext - allocSize)) {
+ mTotalAllocated -= allocSize;
+ mWastedSpace += allocSize;
+ mNext = ptr;
+ }
+}
+
+LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) {
+ pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page));
+ ADD_ALLOCATION(pageSize);
+ mTotalAllocated += pageSize;
+ mPageCount++;
+ void* buf = malloc(pageSize);
+ return new (buf) Page();
+}
+
+static const char* toSize(size_t value, float& result) {
+ if (value < 2000) {
+ result = value;
+ return "B";
+ }
+ if (value < 2000000) {
+ result = value / 1024.0f;
+ return "KB";
+ }
+ result = value / 1048576.0f;
+ return "MB";
+}
+
+void LinearAllocator::dumpMemoryStats(const char* prefix) {
+ float prettySize;
+ const char* prettySuffix;
+ prettySuffix = toSize(mTotalAllocated, prettySize);
+ ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix);
+ prettySuffix = toSize(mWastedSpace, prettySize);
+ ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix,
+ (float) mWastedSpace / (float) mTotalAllocated * 100.0f);
+ ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
+}
+
+}; // namespace android
* limitations under the License.
*/
-#define LOG_TAG "Trace"
-
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/Trace.h>
#include <utils/misc.h>
+#include <utils/Trace.h>
-namespace android {
-
-volatile int32_t Tracer::sIsReady = 0;
-int Tracer::sTraceFD = -1;
-uint64_t Tracer::sEnabledTags = ATRACE_TAG_NOT_READY;
-Mutex Tracer::sMutex;
-
-void Tracer::changeCallback() {
- Mutex::Autolock lock(sMutex);
- if (sIsReady && sTraceFD >= 0) {
- loadSystemProperty();
- }
-}
-
-void Tracer::init() {
- Mutex::Autolock lock(sMutex);
-
- if (!sIsReady) {
- add_sysprop_change_callback(changeCallback, 0);
-
- const char* const traceFileName =
- "/sys/kernel/debug/tracing/trace_marker";
- sTraceFD = open(traceFileName, O_WRONLY);
- if (sTraceFD == -1) {
- ALOGE("error opening trace file: %s (%d)", strerror(errno), errno);
- sEnabledTags = 0; // no tracing can occur
- } else {
- loadSystemProperty();
- }
-
- android_atomic_release_store(1, &sIsReady);
- }
-}
+static void traceInit() __attribute__((constructor));
-void Tracer::loadSystemProperty() {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.atrace.tags.enableflags", value, "0");
- sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK)
- | ATRACE_TAG_ALWAYS;
+static void traceInit()
+{
+ ::android::add_sysprop_change_callback(atrace_update_tags, 0);
}
-
-} // namespace andoid
BasicHashtable_test.cpp \
BlobCache_test.cpp \
Looper_test.cpp \
+ LruCache_test.cpp \
String8_test.cpp \
Unicode_test.cpp \
Vector_test.cpp \
--- /dev/null
+/*
+ * Copyright (C) 2012 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 <stdlib.h>
+#include <utils/JenkinsHash.h>
+#include <utils/LruCache.h>
+#include <cutils/log.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+typedef int SimpleKey;
+typedef const char* StringValue;
+
+struct ComplexKey {
+ int k;
+
+ explicit ComplexKey(int k) : k(k) {
+ instanceCount += 1;
+ }
+
+ ComplexKey(const ComplexKey& other) : k(other.k) {
+ instanceCount += 1;
+ }
+
+ ~ComplexKey() {
+ instanceCount -= 1;
+ }
+
+ bool operator ==(const ComplexKey& other) const {
+ return k == other.k;
+ }
+
+ bool operator !=(const ComplexKey& other) const {
+ return k != other.k;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexKey::instanceCount = 0;
+
+template<> inline hash_t hash_type(const ComplexKey& value) {
+ return hash_type(value.k);
+}
+
+struct ComplexValue {
+ int v;
+
+ explicit ComplexValue(int v) : v(v) {
+ instanceCount += 1;
+ }
+
+ ComplexValue(const ComplexValue& other) : v(other.v) {
+ instanceCount += 1;
+ }
+
+ ~ComplexValue() {
+ instanceCount -= 1;
+ }
+
+ static ssize_t instanceCount;
+};
+
+ssize_t ComplexValue::instanceCount = 0;
+
+typedef LruCache<ComplexKey, ComplexValue> ComplexCache;
+
+class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {
+public:
+ EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { }
+ ~EntryRemovedCallback() {}
+ void operator()(SimpleKey& k, StringValue& v) {
+ callbackCount += 1;
+ lastKey = k;
+ lastValue = v;
+ }
+ ssize_t callbackCount;
+ SimpleKey lastKey;
+ StringValue lastValue;
+};
+
+class LruCacheTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ ComplexKey::instanceCount = 0;
+ ComplexValue::instanceCount = 0;
+ }
+
+ virtual void TearDown() {
+ ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
+ }
+
+ void assertInstanceCount(ssize_t keys, ssize_t values) {
+ if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
+ FAIL() << "Expected " << keys << " keys and " << values << " values "
+ "but there were actually " << ComplexKey::instanceCount << " keys and "
+ << ComplexValue::instanceCount << " values";
+ }
+ }
+};
+
+TEST_F(LruCacheTest, Empty) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ EXPECT_EQ(NULL, cache.get(0));
+ EXPECT_EQ(0u, cache.size());
+}
+
+TEST_F(LruCacheTest, Simple) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_STREQ("one", cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(3u, cache.size());
+}
+
+TEST_F(LruCacheTest, MaxCapacity) {
+ LruCache<SimpleKey, StringValue> cache(2);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(NULL, cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, RemoveLru) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ cache.removeOldest();
+ EXPECT_EQ(NULL, cache.get(1));
+ EXPECT_STREQ("two", cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+TEST_F(LruCacheTest, GetUpdatesLru) {
+ LruCache<SimpleKey, StringValue> cache(100);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_STREQ("one", cache.get(1));
+ cache.removeOldest();
+ EXPECT_STREQ("one", cache.get(1));
+ EXPECT_EQ(NULL, cache.get(2));
+ EXPECT_STREQ("three", cache.get(3));
+ EXPECT_EQ(2u, cache.size());
+}
+
+uint32_t hash_int(int x) {
+ return JenkinsHashWhiten(JenkinsHashMix(0, x));
+}
+
+TEST_F(LruCacheTest, StressTest) {
+ const size_t kCacheSize = 512;
+ LruCache<SimpleKey, StringValue> cache(512);
+ const size_t kNumKeys = 16 * 1024;
+ const size_t kNumIters = 100000;
+ char* strings[kNumKeys];
+
+ for (size_t i = 0; i < kNumKeys; i++) {
+ strings[i] = (char *)malloc(16);
+ sprintf(strings[i], "%d", i);
+ }
+
+ srandom(12345);
+ int hitCount = 0;
+ for (size_t i = 0; i < kNumIters; i++) {
+ int index = random() % kNumKeys;
+ uint32_t key = hash_int(index);
+ const char *val = cache.get(key);
+ if (val != NULL) {
+ EXPECT_EQ(strings[index], val);
+ hitCount++;
+ } else {
+ cache.put(key, strings[index]);
+ }
+ }
+ size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys;
+ EXPECT_LT(int(expectedHitCount * 0.9), hitCount);
+ EXPECT_GT(int(expectedHitCount * 1.1), hitCount);
+ EXPECT_EQ(kCacheSize, cache.size());
+
+ for (size_t i = 0; i < kNumKeys; i++) {
+ free((void *)strings[i]);
+ }
+}
+
+TEST_F(LruCacheTest, NoLeak) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3); // the null value counts as an instance
+}
+
+TEST_F(LruCacheTest, Clear) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.clear();
+ assertInstanceCount(0, 1);
+}
+
+TEST_F(LruCacheTest, ClearNoDoubleFree) {
+ {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.removeOldest();
+ cache.clear();
+ assertInstanceCount(0, 1);
+ }
+ assertInstanceCount(0, 0);
+}
+
+TEST_F(LruCacheTest, ClearReuseOk) {
+ ComplexCache cache(100);
+
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+ cache.clear();
+ assertInstanceCount(0, 1);
+ cache.put(ComplexKey(0), ComplexValue(0));
+ cache.put(ComplexKey(1), ComplexValue(1));
+ EXPECT_EQ(2, cache.size());
+ assertInstanceCount(2, 3);
+}
+
+TEST_F(LruCacheTest, Callback) {
+ LruCache<SimpleKey, StringValue> cache(100);
+ EntryRemovedCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(3, cache.size());
+ cache.removeOldest();
+ EXPECT_EQ(1, callback.callbackCount);
+ EXPECT_EQ(1, callback.lastKey);
+ EXPECT_STREQ("one", callback.lastValue);
+}
+
+TEST_F(LruCacheTest, CallbackOnClear) {
+ LruCache<SimpleKey, StringValue> cache(100);
+ EntryRemovedCallback callback;
+ cache.setOnEntryRemovedListener(&callback);
+
+ cache.put(1, "one");
+ cache.put(2, "two");
+ cache.put(3, "three");
+ EXPECT_EQ(3, cache.size());
+ cache.clear();
+ EXPECT_EQ(3, callback.callbackCount);
+}
+
+}
# Build META EGL library
#
+egl.cfg_config_module :=
+# OpenGL drivers config file
+ifneq ($(BOARD_EGL_CFG),)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := egl.cfg
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/egl
+LOCAL_SRC_FILES := ../../../../$(BOARD_EGL_CFG)
+include $(BUILD_PREBUILT)
+egl.cfg_config_module := $(LOCAL_MODULE)
+endif
+
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
LOCAL_CFLAGS += -DMAX_EGL_CACHE_SIZE=$(MAX_EGL_CACHE_SIZE)
endif
-include $(BUILD_SHARED_LIBRARY)
-installed_libEGL := $(LOCAL_INSTALLED_MODULE)
+LOCAL_REQUIRED_MODULES := $(egl.cfg_config_module)
+egl.cfg_config_module :=
-# OpenGL drivers config file
-ifneq ($(BOARD_EGL_CFG),)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := egl.cfg
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/egl
-LOCAL_SRC_FILES := ../../../../$(BOARD_EGL_CFG)
-include $(BUILD_PREBUILT)
-
-# make sure we depend on egl.cfg, so it gets installed
-$(installed_libEGL): | egl.cfg
-
-endif
+include $(BUILD_SHARED_LIBRARY)
###############################################################################
# Build the wrapper OpenGL ES 1.x library
static bool sEGLSystraceEnabled;
static bool sEGLGetErrorEnabled;
-int gEGLDebugLevel;
-static int sEGLApplicationDebugLevel;
+static volatile int sEGLDebugLevel;
extern gl_hooks_t gHooksTrace;
extern gl_hooks_t gHooksSystrace;
extern gl_hooks_t gHooksErrorTrace;
+int getEGLDebugLevel() {
+ return sEGLDebugLevel;
+}
+
+void setEGLDebugLevel(int level) {
+ sEGLDebugLevel = level;
+}
+
static inline void setGlTraceThreadSpecific(gl_hooks_t const *value) {
pthread_setspecific(gGLTraceKey, value);
}
}
void initEglDebugLevel() {
- int propertyLevel = 0;
- char value[PROPERTY_VALUE_MAX];
-
- // check system property only on userdebug or eng builds
- property_get("ro.debuggable", value, "0");
- if (value[0] == '0')
- return;
+ if (getEGLDebugLevel() == 0) {
+ char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.debug_proc", value, "");
- if (strlen(value) > 0) {
- long pid = getpid();
- char procPath[128] = {};
- sprintf(procPath, "/proc/%ld/cmdline", pid);
- FILE * file = fopen(procPath, "r");
- if (file) {
- char cmdline[256] = {};
- if (fgets(cmdline, sizeof(cmdline) - 1, file)) {
- if (!strncmp(value, cmdline, strlen(value))) {
- // set EGL debug if the "debug.egl.debug_proc" property
- // matches the prefix of this application's command line
- propertyLevel = 1;
+ // check system property only on userdebug or eng builds
+ property_get("ro.debuggable", value, "0");
+ if (value[0] == '0')
+ return;
+
+ property_get("debug.egl.debug_proc", value, "");
+ if (strlen(value) > 0) {
+ FILE * file = fopen("/proc/self/cmdline", "r");
+ if (file) {
+ char cmdline[256];
+ if (fgets(cmdline, sizeof(cmdline), file)) {
+ if (!strncmp(value, cmdline, strlen(value))) {
+ // set EGL debug if the "debug.egl.debug_proc" property
+ // matches the prefix of this application's command line
+ setEGLDebugLevel(1);
+ }
}
+ fclose(file);
}
- fclose(file);
}
}
- gEGLDebugLevel = propertyLevel || sEGLApplicationDebugLevel;
- if (gEGLDebugLevel > 0) {
- GLTrace_start();
+ if (getEGLDebugLevel() > 0) {
+ if (GLTrace_start() < 0) {
+ ALOGE("Error starting Tracer for OpenGL ES. Disabling..");
+ setEGLDebugLevel(0);
+ }
}
}
} else if (sEGLTraceLevel > 0) {
setGlTraceThreadSpecific(value);
setGlThreadSpecific(&gHooksTrace);
- } else if (gEGLDebugLevel > 0 && value != &gHooksNoContext) {
+ } else if (getEGLDebugLevel() > 0 && value != &gHooksNoContext) {
setGlTraceThreadSpecific(value);
setGlThreadSpecific(GLTrace_getGLHooks());
} else {
+ setGlTraceThreadSpecific(NULL);
setGlThreadSpecific(value);
}
}
* Global entry point to allow applications to modify their own debug level.
* Debugging is enabled if either the application requested it, or if the system property
* matches the application's name.
+ * Note that this only sets the debug level. The value is read and used either in
+ * initEglDebugLevel() if the application hasn't initialized its display yet, or when
+ * eglSwapBuffers() is called next.
*/
void EGLAPI setGLDebugLevel(int level) {
- sEGLApplicationDebugLevel = level;
+ setEGLDebugLevel(level);
}
#else
#if EGL_TRACE
pthread_key_create(&gGLTraceKey, NULL);
initEglTraceLevel();
- initEglDebugLevel();
#endif
uint32_t addr = (uint32_t)((void*)gl_no_context);
android_memset32(
extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
extern EGLBoolean egl_init_drivers();
extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
-extern int gEGLDebugLevel;
+extern int getEGLDebugLevel();
+extern void setEGLDebugLevel(int level);
extern gl_hooks_t gHooksTrace;
} // namespace android;
egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
version);
#if EGL_TRACE
- if (gEGLDebugLevel > 0)
+ if (getEGLDebugLevel() > 0)
GLTrace_eglCreateContext(version, c);
#endif
return c;
setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
egl_tls_t::setContext(ctx);
#if EGL_TRACE
- if (gEGLDebugLevel > 0)
+ if (getEGLDebugLevel() > 0)
GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx);
#endif
_c.acquire();
return setError(EGL_BAD_SURFACE, EGL_FALSE);
#if EGL_TRACE
- if (gEGLDebugLevel > 0)
+ gl_hooks_t const *trace_hooks = getGLTraceThreadSpecific();
+ if (getEGLDebugLevel() > 0) {
+ if (trace_hooks == NULL) {
+ if (GLTrace_start() < 0) {
+ ALOGE("Disabling Tracer for OpenGL ES");
+ setEGLDebugLevel(0);
+ } else {
+ // switch over to the trace version of hooks
+ EGLContext ctx = egl_tls_t::getContext();
+ egl_context_t * const c = get_context(ctx);
+ if (c) {
+ setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
+ GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx);
+ }
+ }
+ }
+
GLTrace_eglSwapBuffers(dpy, draw);
+ } else if (trace_hooks != NULL) {
+ // tracing is now disabled, so switch back to the non trace version
+ EGLContext ctx = egl_tls_t::getContext();
+ egl_context_t * const c = get_context(ctx);
+ if (c) setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
+ GLTrace_stop();
+ }
#endif
egl_surface_t const * const s = get_surface(draw);
egl_tls_t::clearTLS();
#if EGL_TRACE
- if (gEGLDebugLevel > 0)
+ if (getEGLDebugLevel() > 0)
GLTrace_eglReleaseThread();
#endif
return EGL_TRUE;
control whether tracing should be enabled for a certain process. If tracing is enabled, this
calls GLTrace_start() to start the trace server.
- Note that initEglTraceLevel() is also called from early_egl_init(), but that happens in the
- context of the zygote, so that invocation has no effect.
-
egl_display_t::initialize() then calls setGLHooksThreadSpecific() where we set the thread
specific gl_hooks structure to point to the trace implementation. From this point on, every
GLES call is redirected to the trace implementation.
to explore if a more graceful method of stopping the application, or detaching tracing from the
application is required.
+
+Enabling tracing while the application is running:
+
+ In order to allow tracing of an already running application, we allow DdmServer to enable
+ OpenGL tracing. In such a case, the application already has its GL hooks set up to point to the
+ real GL implementation, and we need to switch them to point to the trace implementation.
+
+ This is achieved by checking whether tracing should be enabled at every eglSwap call.
+ (Note: We were already checking for tracing at every eglSwap, the only change now is that
+ the tracing could actually be ON/OFF at runtime - earlier it was set once and never changed).
+
+ If eglSwap detects that tracing should be enabled now, then it performs the following steps:
+ - switch the gl hooks to point to the trace implementation.
+ - call trace eglMakeCurrent to indicate that there is now a new context that is current.
+ - continue on with tracing the eglSwap call.
+ This switches the hooks to point to the trace implementation only for the current context.
+ But the other contexts have their gl hooks updated when they perform eglMakeCurrent.
+
+ The GLTrace version of eglMakeCurrent now has to be updated to allow switching to a context
+ it may not know of. In such a case, it creates a context matching the version that it is now
+ switching to.
+
+Disabling tracing:
+
+ We disable tracing under two conditions:
+ - stop tracing request from DdmServer
+ - gltrace transport gets disconnected from the host.
+ In either case, both actions simply disable the tracing flag. The current context gets its
+ gl hooks restored in the next eglSwap, and the other traced contexts get their gl hooks
+ restored when they perform a eglMakeCurrent.
+
Code Structure:
glestrace.h declares all the hooks exposed by libglestrace. These are used by EGL/egl.cpp and
using gltrace::GLTraceContext;
using gltrace::TCPStream;
+static pthread_mutex_t sGlTraceStateLock = PTHREAD_MUTEX_INITIALIZER;
+
+static int sGlTraceInProgress;
static GLTraceState *sGLTraceState;
static pthread_t sReceiveThreadId;
return NULL;
}
-void GLTrace_start() {
- char udsName[PROPERTY_VALUE_MAX];
+/**
+ * Starts Trace Server and waits for connection from the host.
+ * Returns -1 in case of connection error, 0 otherwise.
+ */
+int GLTrace_start() {
+ int status = 0;
+ int clientSocket = -1;
+ TCPStream *stream = NULL;
+
+ pthread_mutex_lock(&sGlTraceStateLock);
+ if (sGlTraceInProgress) {
+ goto done;
+ }
+
+ char udsName[PROPERTY_VALUE_MAX];
property_get("debug.egl.debug_portname", udsName, "gltrace");
- int clientSocket = gltrace::acceptClientConnection(udsName);
+ clientSocket = gltrace::acceptClientConnection(udsName);
if (clientSocket < 0) {
- ALOGE("Error creating GLTrace server socket. Quitting application.");
- exit(-1);
+ ALOGE("Error creating GLTrace server socket. Tracing disabled.");
+ status = -1;
+ goto done;
}
+ sGlTraceInProgress = 1;
+
// create communication channel to the host
- TCPStream *stream = new TCPStream(clientSocket);
+ stream = new TCPStream(clientSocket);
// initialize tracing state
sGLTraceState = new GLTraceState(stream);
pthread_create(&sReceiveThreadId, NULL, commandReceiveTask, sGLTraceState);
+
+done:
+ pthread_mutex_unlock(&sGlTraceStateLock);
+ return status;
}
void GLTrace_stop() {
- delete sGLTraceState;
- sGLTraceState = NULL;
+ pthread_mutex_lock(&sGlTraceStateLock);
+
+ if (sGlTraceInProgress) {
+ sGlTraceInProgress = 0;
+ delete sGLTraceState;
+ sGLTraceState = NULL;
+ }
+
+ pthread_mutex_unlock(&sGlTraceStateLock);
}
void GLTrace_eglCreateContext(int version, EGLContext c) {
+ pthread_mutex_lock(&sGlTraceStateLock);
+ GLTraceState *state = sGLTraceState;
+ pthread_mutex_unlock(&sGlTraceStateLock);
+
+ if (state == NULL) return;
+
// update trace state for new EGL context
- GLTraceContext *traceContext = sGLTraceState->createTraceContext(version, c);
+ GLTraceContext *traceContext = state->createTraceContext(version, c);
gltrace::setupTraceContextThreadSpecific(traceContext);
// trace command through to the host
}
void GLTrace_eglMakeCurrent(const unsigned version, gl_hooks_t *hooks, EGLContext c) {
+ pthread_mutex_lock(&sGlTraceStateLock);
+ GLTraceState *state = sGLTraceState;
+ pthread_mutex_unlock(&sGlTraceStateLock);
+
+ if (state == NULL) return;
+
// setup per context state
- GLTraceContext *traceContext = sGLTraceState->getTraceContext(c);
+ GLTraceContext *traceContext = state->getTraceContext(c);
+ if (traceContext == NULL) {
+ GLTrace_eglCreateContext(version, c);
+ traceContext = state->getTraceContext(c);
+ }
+
traceContext->hooks = hooks;
gltrace::setupTraceContextThreadSpecific(traceContext);
::std::string src = "";
for (int i = 0; i < count; i++) {
if (lengthp != NULL)
- src.append(*stringpp, *lengthp);
+ src.append(*stringpp++, *lengthp++);
else
- src.append(*stringpp); // assume null terminated
- stringpp++;
- lengthp++;
+ src.append(*stringpp++); // assume null terminated
}
arg_strpp->add_charvalue(src);
void GLTrace_eglSwapBuffers(void*, void*);
/* Start and stop GL Tracing. */
-void GLTrace_start();
+int GLTrace_start();
void GLTrace_stop();
/* Obtain the gl_hooks structure filled with the trace implementation for all GL functions. */
# See the License for the specific language governing permissions and
# limitations under the License.
+# Force a specific locale for sorting to avoid irrelevant differences
+# in the generated files that could hide real differences.
+export LC_ALL=POSIX
+
./glapigen ../../include/GLES/gl.h > ../GLES_CM/gl_api.in
./glapigen ../../include/GLES/glext.h > ../GLES_CM/glext_api.in
./glapigen ../../include/GLES2/gl2.h > ../GLES2/gl2_api.in
./glentrygen ../../include/GLES2/gl2.h > /tmp/gl2_entries.in
./glentrygen ../../include/GLES2/gl2ext.h > /tmp/gl2ext_entries.in
+# The awk command removes lines with the same function name as an earlier
+# line, even if the rest of the line differs. Although signatures of
+# functions with the same name should be the same, the ES and ES2 headers
+# have some minor whitespace and parameter name differences.
cat /tmp/gl_entries.in \
/tmp/glext_entries.in \
/tmp/gl2_entries.in \
Status
- Draft.
+ Complete
Version
- Version 1, April 22, 2011
+ Version 3, December 13, 2012
Number
- EGL Extension #XXX
+ EGL Extension #48
Dependencies
New Procedures and Functions
void eglSetBlobCacheFuncsANDROID(EGLDisplay dpy,
- EGLSetBlobFunc set,
- EGLGetBlobFunc get);
+ EGLSetBlobFuncANDROID set,
+ EGLGetBlobFuncANDROID get);
New Tokens
Revision History
+#3 (Jon Leech, December 13, 2012)
+ - Fix typo in New Functions section & assign extension #.
+
#2 (Jamie Gennis, April 25, 2011)
- Swapped the order of the size and pointer arguments to the get and set
functions.
Status
- Draft.
+ Complete
Version
Number
- EGL Extension #XXX
+ EGL Extension #47
Dependencies
--- /dev/null
+Name
+
+ ANDROID_image_native_buffer
+
+Name Strings
+
+ EGL_ANDROID_image_native_buffer
+
+Contributors
+
+ Mathias Agopian
+ Jamie Gennis
+ Jesse Hall
+
+Contact
+
+ Jesse Hall, Google Inc. (jessehall 'at' google.com)
+
+Status
+
+ Complete
+
+Version
+
+ Version 1, November 28, 2012
+
+Number
+
+ EGL Extension #49
+
+Dependencies
+
+ EGL 1.2 is required.
+
+ EGL_KHR_image_base is required.
+
+ This extension is written against the wording of the EGL 1.2
+ Specification.
+
+Overview
+
+ This extension enables using an Android window buffer (struct
+ ANativeWindowBuffer) as an EGLImage source.
+
+New Types
+
+ None.
+
+New Procedures and Functions
+
+ None.
+
+New Tokens
+
+ Accepted by the <target> parameter of eglCreateImageKHR:
+
+ EGL_NATIVE_BUFFER_ANDROID 0x3140
+
+Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors)
+
+ Add to section 2.5.1 "EGLImage Specification" (as defined by the
+ EGL_KHR_image_base specification), in the description of
+ eglCreateImageKHR:
+
+ "Values accepted for <target> are listed in Table aaa, below.
+
+ +----------------------------+-----------------------------------------+
+ | <target> | Notes |
+ +----------------------------+-----------------------------------------+
+ | EGL_NATIVE_BUFFER_ANDROID | Used for ANativeWindowBuffer objects |
+ +----------------------------+-----------------------------------------+
+ Table aaa. Legal values for eglCreateImageKHR <target> parameter
+
+ ...
+
+ If <target> is EGL_NATIVE_BUFFER_ANDROID, <dpy> must be a valid display,
+ <ctx> must be EGL_NO_CONTEXT, <buffer> must be a pointer to a valid
+ ANativeWindowBuffer object (cast into the type EGLClientBuffer), and
+ attributes other than EGL_IMAGE_PRESERVED_KHR are ignored."
+
+ Add to the list of error conditions for eglCreateImageKHR:
+
+ "* If <target> is EGL_NATIVE_BUFFER_ANDROID and <buffer> is not a
+ pointer to a valid ANativeWindowBuffer, the error EGL_BAD_PARAMETER
+ is generated.
+
+ * If <target> is EGL_NATIVE_BUFFER_ANDROID and <ctx> is not
+ EGL_NO_CONTEXT, the error EGL_BAD_CONTEXT is generated.
+
+ * If <target> is EGL_NATIVE_BUFFER_ANDROID and <buffer> was created
+ with properties (format, usage, dimensions, etc.) not supported by
+ the EGL implementation, the error EGL_BAD_PARAMETER is generated."
+
+Issues
+
+ 1. Should this extension define what combinations of ANativeWindowBuffer
+ properties implementations are required to support?
+
+ RESOLVED: No.
+
+ The requirements have evolved over time and will continue to change with
+ future Android releases. The minimum requirements for a given Android
+ version should be documented by that version.
+
+Revision History
+
+#1 (Jesse Hall, November 28, 2012)
+ - Initial draft.
Status
- Draft.
+ Complete
Version
Number
- EGL Extension #XXX
+ EGL Extension #50
Dependencies
Status
- Draft.
+ Complete
Version
- Version 1, July 8, 2011
+ Version 2, July 15, 2011
Number
- EGL Extension #XXX
+ EGL Extension #51
Dependencies
frameworks/native/opengl/libs \
frameworks/native/opengl/libs/EGL \
+# gold in binutils 2.22 will warn about the usage of mktemp
+LOCAL_LDFLAGS += -Wl,--no-fatal-warnings
+
include $(BUILD_EXECUTABLE)
# Include subdirectory makefiles
// Create a EGLSurface
sp<BufferQueue> bq = new BufferQueue();
bq->consumerConnect(new DummyConsumer());
- sp<SurfaceTextureClient> mSTC = new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >( bq));
+ sp<SurfaceTextureClient> mSTC = new SurfaceTextureClient(static_cast<sp<IGraphicBufferProducer> >( bq));
sp<ANativeWindow> mANW = mSTC;
EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config,
-**
+/*
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
#include <EGL/egl.h>
#include <gui/Surface.h>
-#include <gui/SurfaceTexture.h>
+#include <gui/GLConsumer.h>
#include <gui/SurfaceTextureClient.h>
#include <ui/ANativeObjectBase.h>
jint _remaining;
EGLint *attrib_list = (EGLint *) 0;
android::sp<ANativeWindow> window;
- android::sp<android::SurfaceTexture> surfaceTexture;
+ android::sp<android::GLConsumer> glConsumer;
if (!attrib_list_ref) {
_exception = 1;
_exceptionMessage = "Make sure the SurfaceView or associated SurfaceHolder has a valid Surface";
goto exit;
}
- surfaceTexture = android::SurfaceTexture_getSurfaceTexture(_env, win);
- window = new android::SurfaceTextureClient(surfaceTexture);
+ glConsumer = android::SurfaceTexture_getSurfaceTexture(_env, win);
+ window = new android::SurfaceTextureClient(glConsumer);
if (window == NULL)
goto not_valid_surface;
} else if (win instanceof SurfaceHolder) {
SurfaceHolder holder = (SurfaceHolder)win;
sur = holder.getSurface();
+ } else if (win instanceof Surface) {
+ sur = (Surface) win;
}
EGLSurface surface;
} else {
throw new java.lang.UnsupportedOperationException(
"eglCreateWindowSurface() can only be called with an instance of " +
- "SurfaceView, SurfaceTexture or SurfaceHolder at the moment, " +
+ "Surface, SurfaceView, SurfaceTexture or SurfaceHolder at the moment, " +
"this will be fixed later.");
}
// q11 = su^2.dt
//
- // variance of integrated output at 1/dT Hz
- // (random drift)
- const float q00 = gyroVAR * dT;
+ const float dT2 = dT*dT;
+ const float dT3 = dT2*dT;
+
+ // variance of integrated output at 1/dT Hz (random drift)
+ const float q00 = gyroVAR * dT + 0.33333f * biasVAR * dT3;
// variance of drift rate ramp
const float q11 = biasVAR * dT;
-
- const float u = q11 / dT;
- const float q10 = 0.5f*u*dT*dT;
+ const float q10 = 0.5f * biasVAR * dT2;
const float q01 = q10;
GQGt[0][0] = q00; // rad^2
// initial covariance: Var{ x(t0) }
// TODO: initialize P correctly
P = 0;
+
+ // it is unclear how to set the initial covariance. It does affect
+ // how quickly the fusion converges. Experimentally it would take
+ // about 10 seconds at 200 Hz to estimate the gyro-drift with an
+ // initial covariance of 0, and about a second with an initial covariance
+ // of about 1 deg/s.
+ const float covv = 0;
+ const float covu = 0.5f * (float(M_PI) / 180);
+ mat33_t& Pv = P[0][0];
+ Pv[0][0] = covv;
+ Pv[1][1] = covv;
+ Pv[2][2] = covv;
+ mat33_t& Pu = P[1][1];
+ Pu[0][0] = covu;
+ Pu[1][1] = covu;
+ Pu[2][2] = covu;
}
bool Fusion::hasEstimate() const {
mat34_t Fusion::getF(const vec4_t& q) {
mat34_t F;
+
+ // This is used to compute the derivative of q
+ // F = | [q.xyz]x |
+ // | -q.xyz |
+
F[0].x = q.w; F[1].x =-q.z; F[2].x = q.y;
F[0].y = q.z; F[1].y = q.w; F[2].y =-q.x;
F[0].z =-q.y; F[1].z = q.x; F[2].z = q.w;
const vec4_t q = x0;
const vec3_t b = x1;
const vec3_t we = w - b;
- const vec4_t dq = getF(q)*((0.5f*dT)*we);
- x0 = normalize_quat(q + dq);
+ // q(k+1) = O(we)*q(k)
+ // --------------------
+ //
+ // O(w) = | cos(0.5*||w||*dT)*I33 - [psi]x psi |
+ // | -psi' cos(0.5*||w||*dT) |
+ //
+ // psi = sin(0.5*||w||*dT)*w / ||w||
+ //
+ //
// P(k+1) = Phi(k)*P(k)*Phi(k)' + G*Q(k)*G'
+ // ----------------------------------------
//
// G = | -I33 0 |
// | 0 I33 |
const mat33_t wx(crossMatrix(we, 0));
const mat33_t wx2(wx*wx);
const float lwedT = length(we)*dT;
+ const float hlwedT = 0.5f*lwedT;
const float ilwe = 1/length(we);
const float k0 = (1-cosf(lwedT))*(ilwe*ilwe);
const float k1 = sinf(lwedT);
+ const float k2 = cosf(hlwedT);
+ const vec3_t psi(sinf(hlwedT)*ilwe*we);
+ const mat33_t O33(crossMatrix(-psi, k2));
+ mat44_t O;
+ O[0].xyz = O33[0]; O[0].w = -psi.x;
+ O[1].xyz = O33[1]; O[1].w = -psi.y;
+ O[2].xyz = O33[2]; O[2].w = -psi.z;
+ O[3].xyz = psi; O[3].w = k2;
Phi[0][0] = I33 - wx*(k1*ilwe) + wx2*k0;
Phi[1][0] = wx*k0 - I33dT - wx2*(ilwe*ilwe*ilwe)*(lwedT-k1);
+ x0 = O*q;
+ if (x0.w < 0)
+ x0 = -x0;
+
P = Phi*P*transpose(Phi) + GQGt;
checkState();
K[1] = transpose(P[1][0])*LtSi;
// update...
- // P -= K*H*P;
+ // P = (I-K*H) * P
+ // P -= K*H*P
+ // | K0 | * | L 0 | * P = | K0*L 0 | * | P00 P10 | = | K0*L*P00 K0*L*P10 |
+ // | K1 | | K1*L 0 | | P01 P11 | | K1*L*P00 K1*L*P10 |
+ // Note: the Joseph form is numerically more stable and given by:
+ // P = (I-KH) * P * (I-KH)' + K*R*R'
const mat33_t K0L(K[0] * L);
const mat33_t K1L(K[1] * L);
P[0][0] -= K0L*P[0][0];
Client.cpp \
DisplayDevice.cpp \
EventThread.cpp \
+ FrameTracker.cpp \
Layer.cpp \
LayerBase.cpp \
LayerDim.cpp \
LayerScreenshot.cpp \
DisplayHardware/FramebufferSurface.cpp \
- DisplayHardware/GraphicBufferAlloc.cpp \
DisplayHardware/HWComposer.cpp \
DisplayHardware/PowerHAL.cpp \
GLExtensions.cpp \
MessageQueue.cpp \
SurfaceFlinger.cpp \
+ SurfaceFlingerConsumer.cpp \
SurfaceTextureLayer.cpp \
Transform.cpp \
const uint8_t type = mGlobalTransform.getType();
mNeedsFiltering = (!mGlobalTransform.preserveRects() ||
(type >= Transform::SCALE));
+
+ mScissor = mGlobalTransform.transform(mViewport);
+ if (mScissor.isEmpty()) {
+ mScissor.set(getBounds());
+ }
}
}
"+ DisplayDevice: %s\n"
" type=%x, layerStack=%u, (%4dx%4d), ANativeWindow=%p, orient=%2d (type=%08x), "
"flips=%u, isSecure=%d, secureVis=%d, acquired=%d, numLayers=%u\n"
- " v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], "
+ " v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
"transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
mDisplayName.string(), mType,
mLayerStack, mDisplayWidth, mDisplayHeight, mNativeWindow.get(),
mIsSecure, mSecureLayerVisible, mScreenAcquired, mVisibleLayersSortedByZ.size(),
mViewport.left, mViewport.top, mViewport.right, mViewport.bottom,
mFrame.left, mFrame.top, mFrame.right, mFrame.bottom,
+ mScissor.left, mScissor.top, mScissor.right, mScissor.bottom,
tr[0][0], tr[1][0], tr[2][0],
tr[0][1], tr[1][1], tr[2][1],
tr[0][2], tr[1][2], tr[2][2]);
const Transform& getTransform() const { return mGlobalTransform; }
const Rect& getViewport() const { return mViewport; }
const Rect& getFrame() const { return mFrame; }
+ const Rect& getScissor() const { return mScissor; }
bool needsFiltering() const { return mNeedsFiltering; }
uint32_t getLayerStack() const { return mLayerStack; }
uint32_t mLayerStack;
int mOrientation;
+ // user-provided visible area of the layer stack
Rect mViewport;
+ // user-provided rectangle where mViewport gets mapped to
Rect mFrame;
+ // pre-computed scissor to apply to the display
+ Rect mScissor;
Transform mGlobalTransform;
bool mNeedsFiltering;
};
#include <hardware/hardware.h>
#include <gui/SurfaceTextureClient.h>
+#include <gui/GraphicBufferAlloc.h>
#include <ui/GraphicBuffer.h>
#include "DisplayHardware/FramebufferSurface.h"
-#include "DisplayHardware/GraphicBufferAlloc.h"
#include "DisplayHardware/HWComposer.h"
#ifndef NUM_FRAMEBUFFER_SURFACE_BUFFERS
// current buffer is no longer being read. This fence will be returned to
// the producer when the current buffer is released by updateTexImage().
// Multiple fences can be set for a given buffer; they will be merged into
- // a single union fence. The SurfaceTexture will close the file descriptor
+ // a single union fence. The GLConsumer will close the file descriptor
// when finished with it.
status_t setReleaseFenceFd(int fenceFd);
return now - ((now - mLastHwVSync) % mDisplayData[disp].refresh);
}
+sp<Fence> HWComposer::getDisplayFence(int disp) const {
+ return mDisplayData[disp].lastDisplayFence;
+}
+
+
uint32_t HWComposer::getWidth(int disp) const {
return mDisplayData[disp].width;
}
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
disp.framebufferTarget = &disp.list->hwLayers[numLayers - 1];
memset(disp.framebufferTarget, 0, sizeof(hwc_layer_1_t));
- const hwc_rect_t r = { 0, 0, disp.width, disp.height };
+ const hwc_rect_t r = { 0, 0, (int) disp.width, (int) disp.height };
disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET;
disp.framebufferTarget->hints = 0;
disp.framebufferTarget->flags = 0;
}
int acquireFenceFd = -1;
- if (acquireFence != NULL) {
+ if (acquireFence->isValid()) {
acquireFenceFd = acquireFence->dup();
}
for (size_t i=0 ; i<mNumDisplays ; i++) {
DisplayData& disp(mDisplayData[i]);
+ disp.lastDisplayFence = disp.lastRetireFence;
+ disp.lastRetireFence = Fence::NO_FENCE;
if (disp.list) {
if (disp.list->retireFenceFd != -1) {
- close(disp.list->retireFenceFd);
+ disp.lastRetireFence = new Fence(disp.list->retireFenceFd);
disp.list->retireFenceFd = -1;
}
disp.list->flags &= ~HWC_GEOMETRY_CHANGED;
if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
return setFramebufferTarget(id, acquireFence, buffer);
} else {
- if (acquireFence != NULL) {
- acquireFence->waitForever(1000, "HWComposer::fbPost");
- }
+ acquireFence->waitForever(1000, "HWComposer::fbPost");
return mFbDev->post(mFbDev, buffer->handle);
}
}
* This implements the HWCLayer side of HWCIterableLayer.
*/
class HWCLayerVersion1 : public Iterable<HWCLayerVersion1, hwc_layer_1_t> {
+ struct hwc_composer_device_1* mHwc;
public:
- HWCLayerVersion1(hwc_layer_1_t* layer)
- : Iterable<HWCLayerVersion1, hwc_layer_1_t>(layer) { }
+ HWCLayerVersion1(struct hwc_composer_device_1* hwc, hwc_layer_1_t* layer)
+ : Iterable<HWCLayerVersion1, hwc_layer_1_t>(layer), mHwc(hwc) { }
virtual int32_t getCompositionType() const {
return getLayer()->compositionType;
virtual void setPerFrameDefaultState() {
//getLayer()->compositionType = HWC_FRAMEBUFFER;
}
+ virtual void setPlaneAlpha(uint8_t alpha) {
+ if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_2)) {
+ getLayer()->planeAlpha = alpha;
+ } else {
+ if (alpha < 0xFF) {
+ getLayer()->flags |= HWC_SKIP_LAYER;
+ }
+ }
+ }
virtual void setDefaultState() {
- getLayer()->compositionType = HWC_FRAMEBUFFER;
- getLayer()->hints = 0;
- getLayer()->flags = HWC_SKIP_LAYER;
- getLayer()->handle = 0;
- getLayer()->transform = 0;
- getLayer()->blending = HWC_BLENDING_NONE;
- getLayer()->visibleRegionScreen.numRects = 0;
- getLayer()->visibleRegionScreen.rects = NULL;
- getLayer()->acquireFenceFd = -1;
- getLayer()->releaseFenceFd = -1;
+ hwc_layer_1_t* const l = getLayer();
+ l->compositionType = HWC_FRAMEBUFFER;
+ l->hints = 0;
+ l->flags = HWC_SKIP_LAYER;
+ l->handle = 0;
+ l->transform = 0;
+ l->blending = HWC_BLENDING_NONE;
+ l->visibleRegionScreen.numRects = 0;
+ l->visibleRegionScreen.rects = NULL;
+ l->acquireFenceFd = -1;
+ l->releaseFenceFd = -1;
+ l->planeAlpha = 0xFF;
}
virtual void setSkip(bool skip) {
if (skip) {
if (!mHwc || !disp.list || index > disp.list->numHwLayers) {
return LayerListIterator();
}
- return LayerListIterator(new HWCLayerVersion1(disp.list->hwLayers), index);
+ return LayerListIterator(new HWCLayerVersion1(mHwc, disp.list->hwLayers), index);
}
/*
#include <hardware/hwcomposer_defs.h>
+#include <ui/Fence.h>
+
+#include <utils/BitSet.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
#include <utils/Thread.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
-#include <utils/BitSet.h>
extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
const struct timespec *request,
virtual int32_t getCompositionType() const = 0;
virtual uint32_t getHints() const = 0;
virtual int getAndResetReleaseFenceFd() = 0;
- virtual void setPerFrameDefaultState() = 0;
virtual void setDefaultState() = 0;
virtual void setSkip(bool skip) = 0;
virtual void setBlending(uint32_t blending) = 0;
virtual void setVisibleRegionScreen(const Region& reg) = 0;
virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0;
virtual void setAcquireFenceFd(int fenceFd) = 0;
+ virtual void setPlaneAlpha(uint8_t alpha) = 0;
virtual void onDisplayed() = 0;
};
// HWC_DISPLAY_PRIMARY).
nsecs_t getRefreshPeriod(int disp) const;
nsecs_t getRefreshTimestamp(int disp) const;
+ sp<Fence> getDisplayFence(int disp) const;
uint32_t getWidth(int disp) const;
uint32_t getHeight(int disp) const;
uint32_t getFormat(int disp) const;
hwc_display_contents_1* list;
hwc_layer_1* framebufferTarget;
buffer_handle_t fbTargetHandle;
+ sp<Fence> lastRetireFence; // signals when the last set op retires
+ sp<Fence> lastDisplayFence; // signals when the last set op takes
+ // effect on screen
+
// protected by mEventControlLock
int32_t events;
};
--- /dev/null
+/*
+ * Copyright (C) 2012 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 needed for stdint.h to define INT64_MAX in C++
+#define __STDC_LIMIT_MACROS
+
+#include <ui/Fence.h>
+
+#include <utils/String8.h>
+
+#include "FrameTracker.h"
+
+namespace android {
+
+FrameTracker::FrameTracker() :
+ mOffset(0),
+ mNumFences(0) {
+}
+
+void FrameTracker::setDesiredPresentTime(nsecs_t presentTime) {
+ Mutex::Autolock lock(mMutex);
+ mFrameRecords[mOffset].desiredPresentTime = presentTime;
+}
+
+void FrameTracker::setFrameReadyTime(nsecs_t readyTime) {
+ Mutex::Autolock lock(mMutex);
+ mFrameRecords[mOffset].frameReadyTime = readyTime;
+}
+
+void FrameTracker::setFrameReadyFence(const sp<Fence>& readyFence) {
+ Mutex::Autolock lock(mMutex);
+ mFrameRecords[mOffset].frameReadyFence = readyFence;
+ mNumFences++;
+}
+
+void FrameTracker::setActualPresentTime(nsecs_t presentTime) {
+ Mutex::Autolock lock(mMutex);
+ mFrameRecords[mOffset].actualPresentTime = presentTime;
+}
+
+void FrameTracker::setActualPresentFence(const sp<Fence>& readyFence) {
+ Mutex::Autolock lock(mMutex);
+ mFrameRecords[mOffset].actualPresentFence = readyFence;
+ mNumFences++;
+}
+
+void FrameTracker::advanceFrame() {
+ Mutex::Autolock lock(mMutex);
+ mOffset = (mOffset+1) % NUM_FRAME_RECORDS;
+ mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
+ mFrameRecords[mOffset].frameReadyTime = INT64_MAX;
+ mFrameRecords[mOffset].actualPresentTime = INT64_MAX;
+
+ if (mFrameRecords[mOffset].frameReadyFence != NULL) {
+ // We're clobbering an unsignaled fence, so we need to decrement the
+ // fence count.
+ mFrameRecords[mOffset].frameReadyFence = NULL;
+ mNumFences--;
+ }
+
+ if (mFrameRecords[mOffset].actualPresentFence != NULL) {
+ // We're clobbering an unsignaled fence, so we need to decrement the
+ // fence count.
+ mFrameRecords[mOffset].actualPresentFence = NULL;
+ mNumFences--;
+ }
+
+ // Clean up the signaled fences to keep the number of open fence FDs in
+ // this process reasonable.
+ processFencesLocked();
+}
+
+void FrameTracker::clear() {
+ Mutex::Autolock lock(mMutex);
+ for (size_t i = 0; i < NUM_FRAME_RECORDS; i++) {
+ mFrameRecords[i].desiredPresentTime = 0;
+ mFrameRecords[i].frameReadyTime = 0;
+ mFrameRecords[i].actualPresentTime = 0;
+ mFrameRecords[i].frameReadyFence.clear();
+ mFrameRecords[i].actualPresentFence.clear();
+ }
+ mNumFences = 0;
+ mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
+ mFrameRecords[mOffset].frameReadyTime = INT64_MAX;
+ mFrameRecords[mOffset].actualPresentTime = INT64_MAX;
+}
+
+void FrameTracker::processFencesLocked() const {
+ FrameRecord* records = const_cast<FrameRecord*>(mFrameRecords);
+ int& numFences = const_cast<int&>(mNumFences);
+
+ for (int i = 1; i < NUM_FRAME_RECORDS && numFences > 0; i++) {
+ size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;
+
+ const sp<Fence>& rfence = records[idx].frameReadyFence;
+ if (rfence != NULL) {
+ records[idx].frameReadyTime = rfence->getSignalTime();
+ if (records[idx].frameReadyTime < INT64_MAX) {
+ records[idx].frameReadyFence = NULL;
+ numFences--;
+ }
+ }
+
+ const sp<Fence>& pfence = records[idx].actualPresentFence;
+ if (pfence != NULL) {
+ records[idx].actualPresentTime = pfence->getSignalTime();
+ if (records[idx].actualPresentTime < INT64_MAX) {
+ records[idx].actualPresentFence = NULL;
+ numFences--;
+ }
+ }
+ }
+}
+
+void FrameTracker::dump(String8& result) const {
+ Mutex::Autolock lock(mMutex);
+ processFencesLocked();
+
+ const size_t o = mOffset;
+ for (size_t i = 1; i < NUM_FRAME_RECORDS; i++) {
+ const size_t index = (o+i) % NUM_FRAME_RECORDS;
+ result.appendFormat("%lld\t%lld\t%lld\n",
+ mFrameRecords[index].desiredPresentTime,
+ mFrameRecords[index].actualPresentTime,
+ mFrameRecords[index].frameReadyTime);
+ }
+ result.append("\n");
+}
+
+} // namespace android
--- /dev/null
+/*
+ * Copyright (C) 2012 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 ANDROID_FRAMETRACKER_H
+#define ANDROID_FRAMETRACKER_H
+
+#include <stddef.h>
+
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class String8;
+class Fence;
+
+// FrameTracker tracks information about the most recently rendered frames. It
+// uses a circular buffer of frame records, and is *NOT* thread-safe -
+// mutexing must be done at a higher level if multi-threaded access is
+// possible.
+//
+// Some of the time values tracked may be set either as a specific timestamp
+// or a fence. When a non-NULL fence is set for a given time value, the
+// signal time of that fence is used instead of the timestamp.
+class FrameTracker {
+
+public:
+ // NUM_FRAME_RECORDS is the size of the circular buffer used to track the
+ // frame time history.
+ enum { NUM_FRAME_RECORDS = 128 };
+
+ FrameTracker();
+
+ // setDesiredPresentTime sets the time at which the current frame
+ // should be presented to the user under ideal (i.e. zero latency)
+ // conditions.
+ void setDesiredPresentTime(nsecs_t desiredPresentTime);
+
+ // setFrameReadyTime sets the time at which the current frame became ready
+ // to be presented to the user. For example, if the frame contents is
+ // being written to memory by some asynchronous hardware, this would be
+ // the time at which those writes completed.
+ void setFrameReadyTime(nsecs_t readyTime);
+
+ // setFrameReadyFence sets the fence that is used to get the time at which
+ // the current frame became ready to be presented to the user.
+ void setFrameReadyFence(const sp<Fence>& readyFence);
+
+ // setActualPresentTime sets the timestamp at which the current frame became
+ // visible to the user.
+ void setActualPresentTime(nsecs_t displayTime);
+
+ // setActualPresentFence sets the fence that is used to get the time
+ // at which the current frame became visible to the user.
+ void setActualPresentFence(const sp<Fence>& fence);
+
+ // advanceFrame advances the frame tracker to the next frame.
+ void advanceFrame();
+
+ // clear resets all the tracked frame data to zero.
+ void clear();
+
+ // dump appends the current frame display time history to the result string.
+ void dump(String8& result) const;
+
+private:
+ struct FrameRecord {
+ FrameRecord() :
+ desiredPresentTime(0),
+ frameReadyTime(0),
+ actualPresentTime(0) {}
+ nsecs_t desiredPresentTime;
+ nsecs_t frameReadyTime;
+ nsecs_t actualPresentTime;
+ sp<Fence> frameReadyFence;
+ sp<Fence> actualPresentFence;
+ };
+
+ // processFences iterates over all the frame records that have a fence set
+ // and replaces that fence with a timestamp if the fence has signaled. If
+ // the fence is not signaled the record's displayTime is set to INT64_MAX.
+ //
+ // This method is const because although it modifies the frame records it
+ // does so in such a way that the information represented should not
+ // change. This allows it to be called from the dump method.
+ void processFencesLocked() const;
+
+ // mFrameRecords is the circular buffer storing the tracked data for each
+ // frame.
+ FrameRecord mFrameRecords[NUM_FRAME_RECORDS];
+
+ // mOffset is the offset into mFrameRecords of the current frame.
+ size_t mOffset;
+
+ // mNumFences is the total number of fences set in the frame records. It
+ // is incremented each time a fence is added and decremented each time a
+ // signaled fence is removed in processFences or if advanceFrame clobbers
+ // a fence.
+ //
+ // The number of fences is tracked so that the run time of processFences
+ // doesn't grow with NUM_FRAME_RECORDS.
+ int mNumFences;
+
+ // mMutex is used to protect access to all member variables.
+ mutable Mutex mMutex;
+};
+
+}
+
+#endif // ANDROID_FRAMETRACKER_H
mCurrentOpacity(true),
mRefreshPending(false),
mFrameLatencyNeeded(false),
- mFrameLatencyOffset(0),
mFormat(PIXEL_FORMAT_NONE),
mGLExtensions(GLExtensions::getInstance()),
mOpaqueLayer(true),
HWComposer::HWCLayerInterface* layer) {
LayerBaseClient::onLayerDisplayed(hw, layer);
if (layer) {
- mSurfaceTexture->setReleaseFence(layer->getAndResetReleaseFenceFd());
+ mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFenceFd());
}
}
{
LayerBaseClient::onFirstRef();
- struct FrameQueuedListener : public SurfaceTexture::FrameAvailableListener {
- FrameQueuedListener(Layer* layer) : mLayer(layer) { }
- private:
- wp<Layer> mLayer;
- virtual void onFrameAvailable() {
- sp<Layer> that(mLayer.promote());
- if (that != 0) {
- that->onFrameQueued();
- }
- }
- };
-
- // Creates a custom BufferQueue for SurfaceTexture to use
+ // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
sp<BufferQueue> bq = new SurfaceTextureLayer();
- mSurfaceTexture = new SurfaceTexture(mTextureName, true,
+ mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(mTextureName, true,
GL_TEXTURE_EXTERNAL_OES, false, bq);
- mSurfaceTexture->setConsumerUsageBits(getEffectiveUsage(0));
- mSurfaceTexture->setFrameAvailableListener(new FrameQueuedListener(this));
- mSurfaceTexture->setSynchronousMode(true);
+ mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
+ mSurfaceFlingerConsumer->setFrameAvailableListener(this);
+ mSurfaceFlingerConsumer->setSynchronousMode(true);
#ifdef TARGET_DISABLE_TRIPLE_BUFFERING
#warning "disabling triple buffering"
- mSurfaceTexture->setDefaultMaxBufferCount(2);
+ mSurfaceFlingerConsumer->setDefaultMaxBufferCount(2);
#else
- mSurfaceTexture->setDefaultMaxBufferCount(3);
+ mSurfaceFlingerConsumer->setDefaultMaxBufferCount(3);
#endif
const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
mFlinger->deleteTextureAsync(mTextureName);
}
-void Layer::onFrameQueued() {
+void Layer::onFrameAvailable() {
android_atomic_inc(&mQueuedFrames);
mFlinger->signalLayerUpdate();
}
// in the purgatory list
void Layer::onRemoved()
{
- mSurfaceTexture->abandon();
+ mSurfaceFlingerConsumer->abandon();
}
void Layer::setName(const String8& name) {
LayerBase::setName(name);
- mSurfaceTexture->setName(name);
+ mSurfaceFlingerConsumer->setName(name);
}
sp<ISurface> Layer::createSurface()
{
+ /*
+ * This class provides an implementation of BnSurface (the "native" or
+ * "remote" side of the Binder IPC interface ISurface), and mixes in
+ * LayerCleaner to ensure that mFlinger->onLayerDestroyed() is called for
+ * this layer when the BSurface is destroyed.
+ *
+ * The idea is to provide a handle to the Layer through ISurface that
+ * is cleaned up automatically when the last reference to the ISurface
+ * goes away. (The references will be held on the "proxy" side, while
+ * the Layer exists on the "native" side.)
+ *
+ * The Layer has a reference to an instance of SurfaceFlinger's variant
+ * of GLConsumer, which holds a reference to the BufferQueue. The
+ * getSurfaceTexture() call returns a Binder interface reference for
+ * the producer interface of the buffer queue associated with the Layer.
+ */
class BSurface : public BnSurface, public LayerCleaner {
wp<const Layer> mOwner;
- virtual sp<ISurfaceTexture> getSurfaceTexture() const {
- sp<ISurfaceTexture> res;
+ virtual sp<IGraphicBufferProducer> getSurfaceTexture() const {
+ sp<IGraphicBufferProducer> res;
sp<const Layer> that( mOwner.promote() );
if (that != NULL) {
- res = that->mSurfaceTexture->getBufferQueue();
+ res = that->mSurfaceFlingerConsumer->getBufferQueue();
}
return res;
}
wp<IBinder> Layer::getSurfaceTextureBinder() const
{
- return mSurfaceTexture->getBufferQueue()->asBinder();
+ return mSurfaceFlingerConsumer->getBufferQueue()->asBinder();
}
status_t Layer::setBuffers( uint32_t w, uint32_t h,
mOpaqueLayer = (flags & ISurfaceComposerClient::eOpaque);
mCurrentOpacity = getOpacityForFormat(format);
- mSurfaceTexture->setDefaultBufferSize(w, h);
- mSurfaceTexture->setDefaultBufferFormat(format);
- mSurfaceTexture->setConsumerUsageBits(getEffectiveUsage(0));
+ mSurfaceFlingerConsumer->setDefaultBufferSize(w, h);
+ mSurfaceFlingerConsumer->setDefaultBufferFormat(format);
+ mSurfaceFlingerConsumer->setConsumerUsageBits(getEffectiveUsage(0));
return NO_ERROR;
}
Rect Layer::computeBufferCrop() const {
- // Start with the SurfaceTexture's buffer crop...
+ // Start with the SurfaceFlingerConsumer's buffer crop...
Rect crop;
if (!mCurrentCrop.isEmpty()) {
crop = mCurrentCrop;
if (!s.active.crop.isEmpty()) {
// Transform the window crop to match the buffer coordinate system,
// which means using the inverse of the current transform set on the
- // SurfaceTexture.
+ // SurfaceFlingerConsumer.
uint32_t invTransform = mCurrentTransform;
int winWidth = s.active.w;
int winHeight = s.active.h;
// enable this layer
layer.setSkip(false);
- // we can't do alpha-fade with the hwc HAL
- const State& s(drawingState());
- if (s.alpha < 0xFF) {
- layer.setSkip(true);
- }
-
if (isSecure() && !hw->isSecure()) {
layer.setSkip(true);
}
+ const State& s(drawingState());
+ layer.setPlaneAlpha(s.alpha);
+
/*
* Transformations are applied in this order:
* 1) buffer orientation/flip/mirror
// acquire fence the first time a new buffer is acquired on EACH display.
if (layer.getCompositionType() == HWC_OVERLAY) {
- sp<Fence> fence = mSurfaceTexture->getCurrentFence();
- if (fence.get()) {
+ sp<Fence> fence = mSurfaceFlingerConsumer->getCurrentFence();
+ if (fence->isValid()) {
fenceFd = fence->dup();
if (fenceFd == -1) {
ALOGW("failed to dup layer fence, skipping sync: %d", errno);
return;
}
- status_t err = mSurfaceTexture->doGLFenceWait();
- if (err != OK) {
- ALOGE("onDraw: failed waiting for fence: %d", err);
+ // Bind the current buffer to the GL texture, and wait for it to be
+ // ready for us to draw into.
+ status_t err = mSurfaceFlingerConsumer->bindTextureImage();
+ if (err != NO_ERROR) {
+ ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
// Go ahead and draw the buffer anyway; no matter what we do the screen
// is probably going to have something visibly wrong.
}
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
- mSurfaceTexture->setFilteringEnabled(useFiltering);
- mSurfaceTexture->getTransformMatrix(textureMatrix);
+ mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering);
+ mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
// Set things up for texturing.
glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureName);
// record the new size, form this point on, when the client request
// a buffer, it'll get the new size.
- mSurfaceTexture->setDefaultBufferSize(
+ mSurfaceFlingerConsumer->setDefaultBufferSize(
temp.requested.w, temp.requested.h);
}
void Layer::onPostComposition() {
if (mFrameLatencyNeeded) {
+ nsecs_t desiredPresentTime = mSurfaceFlingerConsumer->getTimestamp();
+ mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+
+ sp<Fence> frameReadyFence = mSurfaceFlingerConsumer->getCurrentFence();
+ if (frameReadyFence != NULL) {
+ mFrameTracker.setFrameReadyFence(frameReadyFence);
+ } else {
+ // There was no fence for this frame, so assume that it was ready
+ // to be presented at the desired present time.
+ mFrameTracker.setFrameReadyTime(desiredPresentTime);
+ }
+
const HWComposer& hwc = mFlinger->getHwComposer();
- const size_t offset = mFrameLatencyOffset;
- mFrameStats[offset].timestamp = mSurfaceTexture->getTimestamp();
- mFrameStats[offset].set = systemTime();
- mFrameStats[offset].vsync = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
- mFrameLatencyOffset = (mFrameLatencyOffset + 1) % 128;
+ sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+ if (presentFence != NULL) {
+ mFrameTracker.setActualPresentFence(presentFence);
+ } else {
+ // The HWC doesn't support present fences, so use the refresh
+ // timestamp instead.
+ nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
+ mFrameTracker.setActualPresentTime(presentTime);
+ }
+
+ mFrameTracker.advanceFrame();
mFrameLatencyNeeded = false;
}
}
mFlinger->signalLayerUpdate();
}
- struct Reject : public SurfaceTexture::BufferRejecter {
+ struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
Layer::State& front;
Layer::State& current;
bool& recomputeVisibleRegions;
Reject r(mDrawingState, currentState(), recomputeVisibleRegions);
- if (mSurfaceTexture->updateTexImage(&r, true) < NO_ERROR) {
+ if (mSurfaceFlingerConsumer->updateTexImage(&r) != NO_ERROR) {
// something happened!
recomputeVisibleRegions = true;
return outDirtyRegion;
}
// update the active buffer
- mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
+ mActiveBuffer = mSurfaceFlingerConsumer->getCurrentBuffer();
if (mActiveBuffer == NULL) {
// this can only happen if the very first buffer was rejected.
return outDirtyRegion;
recomputeVisibleRegions = true;
}
- Rect crop(mSurfaceTexture->getCurrentCrop());
- const uint32_t transform(mSurfaceTexture->getCurrentTransform());
- const uint32_t scalingMode(mSurfaceTexture->getCurrentScalingMode());
+ Rect crop(mSurfaceFlingerConsumer->getCurrentCrop());
+ const uint32_t transform(mSurfaceFlingerConsumer->getCurrentTransform());
+ const uint32_t scalingMode(mSurfaceFlingerConsumer->getCurrentScalingMode());
if ((crop != mCurrentCrop) ||
(transform != mCurrentTransform) ||
(scalingMode != mCurrentScalingMode))
result.append(buffer);
- if (mSurfaceTexture != 0) {
- mSurfaceTexture->dump(result, " ", buffer, SIZE);
+ if (mSurfaceFlingerConsumer != 0) {
+ mSurfaceFlingerConsumer->dump(result, " ", buffer, SIZE);
}
}
void Layer::dumpStats(String8& result, char* buffer, size_t SIZE) const
{
LayerBaseClient::dumpStats(result, buffer, SIZE);
- const size_t o = mFrameLatencyOffset;
- const nsecs_t period =
- mFlinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
- result.appendFormat("%lld\n", period);
- for (size_t i=0 ; i<128 ; i++) {
- const size_t index = (o+i) % 128;
- const nsecs_t time_app = mFrameStats[index].timestamp;
- const nsecs_t time_set = mFrameStats[index].set;
- const nsecs_t time_vsync = mFrameStats[index].vsync;
- result.appendFormat("%lld\t%lld\t%lld\n",
- time_app,
- time_vsync,
- time_set);
- }
- result.append("\n");
+ mFrameTracker.dump(result);
}
void Layer::clearStats()
{
LayerBaseClient::clearStats();
- memset(mFrameStats, 0, sizeof(mFrameStats));
+ mFrameTracker.clear();
}
uint32_t Layer::getEffectiveUsage(uint32_t usage) const
orientation = 0;
}
}
- mSurfaceTexture->setTransformHint(orientation);
+ mSurfaceFlingerConsumer->setTransformHint(orientation);
}
// ---------------------------------------------------------------------------
#include <stdint.h>
#include <sys/types.h>
-#include <gui/SurfaceTexture.h>
-
#include <utils/Timers.h>
#include <ui/GraphicBuffer.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
+#include "SurfaceFlingerConsumer.h"
+#include "FrameTracker.h"
#include "LayerBase.h"
#include "SurfaceTextureLayer.h"
#include "Transform.h"
// ---------------------------------------------------------------------------
-class Layer : public LayerBaseClient
+/*
+ * The Layer class is essentially a LayerBase combined with a BufferQueue.
+ * A new BufferQueue and a new SurfaceFlingerConsumer are created when the
+ * Layer is first referenced.
+ *
+ * This also implements onFrameAvailable(), which notifies SurfaceFlinger
+ * that new data has arrived.
+ */
+class Layer : public LayerBaseClient,
+ public SurfaceFlingerConsumer::FrameAvailableListener
{
public:
- Layer(SurfaceFlinger* flinger, const sp<Client>& client);
+ Layer(SurfaceFlinger* flinger, const sp<Client>& client);
virtual ~Layer();
virtual const char* getTypeId() const { return "Layer"; }
// only for debugging
inline const sp<GraphicBuffer>& getActiveBuffer() const { return mActiveBuffer; }
- // Updates the transform hint in our SurfaceTexture to match
+ // Updates the transform hint in our SurfaceFlingerConsumer to match
// the current orientation of the display device.
virtual void updateTransformHint(const sp<const DisplayDevice>& hw) const;
virtual void clearStats();
private:
- friend class SurfaceTextureLayer;
- void onFrameQueued();
+ // Creates an instance of ISurface for this Layer.
virtual sp<ISurface> createSurface();
+
uint32_t getEffectiveUsage(uint32_t usage) const;
bool isCropped() const;
Rect computeBufferCrop() const;
static bool getOpacityForFormat(uint32_t format);
+ // Interface implementation for SurfaceFlingerConsumer::FrameAvailableListener
+ virtual void onFrameAvailable();
+
// -----------------------------------------------------------------------
// constants
- sp<SurfaceTexture> mSurfaceTexture;
+ sp<SurfaceFlingerConsumer> mSurfaceFlingerConsumer;
GLuint mTextureName;
// thread-safe
volatile int32_t mQueuedFrames;
+ FrameTracker mFrameTracker;
// main thread
sp<GraphicBuffer> mActiveBuffer;
bool mCurrentOpacity;
bool mRefreshPending;
bool mFrameLatencyNeeded;
- int mFrameLatencyOffset;
-
- struct Statistics {
- Statistics() : timestamp(0), set(0), vsync(0) { }
- nsecs_t timestamp; // buffer timestamp
- nsecs_t set; // buffer displayed timestamp
- nsecs_t vsync; // vsync immediately before set
- };
-
- // protected by mLock
- Statistics mFrameStats[128];
// constants
PixelFormat mFormat;
layer.setTransform(finalTransform);
}
- if (!isOpaque()) {
+ if (!isOpaque() || s.alpha != 0xFF) {
layer.setBlending(mPremultipliedAlpha ?
HWC_BLENDING_PREMULT :
HWC_BLENDING_COVERAGE);
void LayerBase::setPerFrameData(const sp<const DisplayDevice>& hw,
HWComposer::HWCLayerInterface& layer) {
- layer.setPerFrameDefaultState();
// we have to set the visible region on every frame because
// we currently free it during onLayerDisplayed(), which is called
// after HWComposer::commit() -- every frame.
sp<ISurface> LayerBaseClient::createSurface()
{
class BSurface : public BnSurface, public LayerCleaner {
- virtual sp<ISurfaceTexture> getSurfaceTexture() const { return 0; }
+ virtual sp<IGraphicBufferProducer> getSurfaceTexture() const { return 0; }
public:
BSurface(const sp<SurfaceFlinger>& flinger,
const sp<LayerBaseClient>& layer)
// ---------------------------------------------------------------------------
-class LayerBase : public RefBase
+/*
+ * Layers are rectangular graphic entities, internal to SurfaceFlinger.
+ * They have properties including width, height, Z-depth, and 2D
+ * transformations (chiefly translation and 90-degree rotations).
+ *
+ * Layers are organized into "layer stacks". Each layer is a member of
+ * exactly one layer stack, identified by an integer in Layer::State. A
+ * given layer stack may appear on more than one display.
+ *
+ * Notable subclasses (below LayerBaseClient) include Layer, LayerDim, and
+ * LayerScreenshot.
+ */
+class LayerBase : virtual public RefBase
{
static int32_t sSequence;
virtual void onPostComposition() { }
/**
- * Updates the SurfaceTexture's transform hint, for layers that have
- * a SurfaceTexture.
+ * Updates the GLConsumer's transform hint, for layers that have
+ * a GLConsumer.
*/
virtual void updateTransformHint(const sp<const DisplayDevice>& hw) const { }
// ---------------------------------------------------------------------------
+/*
+ * This adds some additional fields and methods to support some Binder IPC
+ * interactions. In particular, the LayerBaseClient's lifetime can be
+ * managed by references to an ISurface object in another process.
+ */
class LayerBaseClient : public LayerBase
{
public:
- LayerBaseClient(SurfaceFlinger* flinger, const sp<Client>& client);
+ LayerBaseClient(SurfaceFlinger* flinger, const sp<Client>& client);
+
+ virtual ~LayerBaseClient();
- virtual ~LayerBaseClient();
+ // Creates an ISurface associated with this object. This may only be
+ // called once (see also getSurfaceBinder()).
+ sp<ISurface> getSurface();
- sp<ISurface> getSurface();
- wp<IBinder> getSurfaceBinder() const;
- virtual wp<IBinder> getSurfaceTextureBinder() const;
+ // Returns the Binder object for the ISurface associated with
+ // this object.
+ wp<IBinder> getSurfaceBinder() const;
+
+ virtual wp<IBinder> getSurfaceTextureBinder() const;
virtual sp<LayerBaseClient> getLayerBaseClient() const {
return const_cast<LayerBaseClient*>(this); }
virtual void dump(String8& result, char* scratch, size_t size) const;
virtual void shortDump(String8& result, char* scratch, size_t size) const;
+ /*
+ * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
+ * is called.
+ */
class LayerCleaner {
sp<SurfaceFlinger> mFlinger;
wp<LayerBaseClient> mLayer;
virtual sp<ISurface> createSurface();
mutable Mutex mLock;
+
+ // Set to true if an ISurface has been associated with this object.
mutable bool mHasSurface;
+
+ // The ISurface's Binder object, set by getSurface().
wp<IBinder> mClientSurfaceBinder;
+
const wp<Client> mClientRef;
// only read
const uint32_t mIdentity;
#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/SurfaceTextureClient.h>
+#include <gui/GraphicBufferAlloc.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
#include "SurfaceFlinger.h"
#include "DisplayHardware/FramebufferSurface.h"
-#include "DisplayHardware/GraphicBufferAlloc.h"
#include "DisplayHardware/HWComposer.h"
mBootTime(systemTime()),
mVisibleRegionsDirty(false),
mHwWorkListDirty(false),
+ mAnimCompositionPending(false),
mDebugRegion(0),
mDebugDDMS(0),
mDebugDisableHWC(0),
sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i);
sp<SurfaceTextureClient> stc = new SurfaceTextureClient(
- static_cast< sp<ISurfaceTexture> >(fbs->getBufferQueue()));
+ static_cast< sp<IGraphicBufferProducer> >(fbs->getBufferQueue()));
sp<DisplayDevice> hw = new DisplayDevice(this,
type, isSecure, token, stc, fbs, mEGLConfig);
if (i > DisplayDevice::DISPLAY_PRIMARY) {
// ----------------------------------------------------------------------------
bool SurfaceFlinger::authenticateSurfaceTexture(
- const sp<ISurfaceTexture>& surfaceTexture) const {
+ const sp<IGraphicBufferProducer>& bufferProducer) const {
Mutex::Autolock _l(mStateLock);
- sp<IBinder> surfaceTextureBinder(surfaceTexture->asBinder());
+ sp<IBinder> surfaceTextureBinder(bufferProducer->asBinder());
- // Check the visible layer list for the ISurface
+ // We want to determine whether the IGraphicBufferProducer was created by
+ // SurfaceFlinger. Check to see if we can find it in the layer list.
const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
size_t count = currentLayers.size();
for (size_t i=0 ; i<count ; i++) {
const sp<LayerBase>& layer(currentLayers[i]);
sp<LayerBaseClient> lbc(layer->getLayerBaseClient());
if (lbc != NULL) {
+ // If this is an instance of Layer (as opposed to, say, LayerDim),
+ // we will get the consumer interface of SurfaceFlingerConsumer's
+ // BufferQueue. If it's the same Binder object as the graphic
+ // buffer producer interface, return success.
wp<IBinder> lbcBinder = lbc->getSurfaceTextureBinder();
if (lbcBinder == surfaceTextureBinder) {
return true;
}
// Check the layers in the purgatory. This check is here so that if a
- // SurfaceTexture gets destroyed before all the clients are done using it,
+ // GLConsumer gets destroyed before all the clients are done using it,
// the error will not be reported as "surface XYZ is not authenticated", but
// will instead fail later on when the client tries to use the surface,
// which should be reported as "surface XYZ returned an -ENODEV". The
while (it != end) {
const Rect& r = *it++;
GLfloat vertices[][2] = {
- { r.left, height - r.top },
- { r.left, height - r.bottom },
- { r.right, height - r.bottom },
- { r.right, height - r.top }
+ { (GLfloat) r.left, (GLfloat) (height - r.top) },
+ { (GLfloat) r.left, (GLfloat) (height - r.bottom) },
+ { (GLfloat) r.right, (GLfloat) (height - r.bottom) },
+ { (GLfloat) r.right, (GLfloat) (height - r.top) }
};
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
for (size_t i=0 ; i<count ; i++) {
currentLayers[i]->onPostComposition();
}
+
+ if (mAnimCompositionPending) {
+ mAnimCompositionPending = false;
+
+ const HWComposer& hwc = getHwComposer();
+ sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+ if (presentFence != NULL) {
+ mAnimFrameTracker.setActualPresentFence(presentFence);
+ } else {
+ // The HWC doesn't support present fences, so use the refresh
+ // timestamp instead.
+ nsecs_t presentTime = hwc.getRefreshTimestamp(HWC_DISPLAY_PRIMARY);
+ mAnimFrameTracker.setActualPresentTime(presentTime);
+ }
+ mAnimFrameTracker.advanceFrame();
+ }
}
void SurfaceFlinger::rebuildLayerStacks() {
// own rendering surface
fbs = new FramebufferSurface(*mHwc, state.type);
stc = new SurfaceTextureClient(
- static_cast< sp<ISurfaceTexture> >(
+ static_cast< sp<IGraphicBufferProducer> >(
fbs->getBufferQueue()));
} else {
if (state.surface != NULL) {
mLayersPendingRemoval.clear();
}
+ // If this transaction is part of a window animation then the next frame
+ // we composite should be considered an animation as well.
+ mAnimCompositionPending = mAnimTransactionPending;
+
mDrawingState = mCurrentState;
mTransactionPending = false;
mAnimTransactionPending = false;
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
} else {
- const Region region(hw->undefinedRegion.intersect(dirty));
+ // we start with the whole screen area
+ const Region bounds(hw->getBounds());
+
+ // we remove the scissor part
+ // we're left with the letterbox region
+ // (common case is that letterbox ends-up being empty)
+ const Region letterbox(bounds.subtract(hw->getScissor()));
+
+ // compute the area to clear
+ Region region(hw->undefinedRegion.merge(letterbox));
+
+ // but limit it to the dirty region
+ region.andSelf(dirty);
+
// screen is already cleared here
if (!region.isEmpty()) {
// can happen with SurfaceView
}
}
- if (hw->getDisplayType() >= DisplayDevice::DISPLAY_EXTERNAL) {
- // TODO: just to be on the safe side, we don't set the
+ if (hw->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) {
+ // just to be on the safe side, we don't set the
// scissor on the main display. It should never be needed
// anyways (though in theory it could since the API allows it).
const Rect& bounds(hw->getBounds());
- const Transform& tr(hw->getTransform());
- const Rect scissor(tr.transform(hw->getViewport()));
+ const Rect& scissor(hw->getScissor());
if (scissor != bounds) {
// scissor doesn't match the screen's dimensions, so we
// need to clear everything outside of it and enable
const GLint height = hw->getHeight();
glScissor(scissor.left, height - scissor.bottom,
scissor.getWidth(), scissor.getHeight());
- // clear everything unscissored
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
// enable scissor for this frame
glEnable(GL_SCISSOR_TEST);
}
while (it != end) {
const Rect& r = *it++;
GLfloat vertices[][2] = {
- { r.left, height - r.top },
- { r.left, height - r.bottom },
- { r.right, height - r.bottom },
- { r.right, height - r.top }
+ { (GLfloat) r.left, (GLfloat) (height - r.top) },
+ { (GLfloat) r.left, (GLfloat) (height - r.bottom) },
+ { (GLfloat) r.right, (GLfloat) (height - r.bottom) },
+ { (GLfloat) r.right, (GLfloat) (height - r.top) }
};
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
index++;
}
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- const size_t count = currentLayers.size();
- for (size_t i=0 ; i<count ; i++) {
- const sp<LayerBase>& layer(currentLayers[i]);
- if (name.isEmpty()) {
- snprintf(buffer, SIZE, "%s\n", layer->getName().string());
- result.append(buffer);
- }
- if (name.isEmpty() || (name == layer->getName())) {
- layer->dumpStats(result, buffer, SIZE);
+ const nsecs_t period =
+ getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
+ result.appendFormat("%lld\n", period);
+
+ if (name.isEmpty()) {
+ mAnimFrameTracker.dump(result);
+ } else {
+ const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ const size_t count = currentLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(currentLayers[i]);
+ if (name == layer->getName()) {
+ layer->dumpStats(result, buffer, SIZE);
+ }
}
}
}
void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index,
- String8& result, char* buffer, size_t SIZE) const
+ String8& result, char* buffer, size_t SIZE)
{
String8 name;
if (index < args.size()) {
layer->clearStats();
}
}
+
+ mAnimFrameTracker.clear();
}
/*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
#include <private/gui/LayerState.h>
#include "Barrier.h"
-#include "MessageQueue.h"
#include "DisplayDevice.h"
+#include "FrameTracker.h"
+#include "MessageQueue.h"
#include "DisplayHardware/HWComposer.h"
bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
DisplayDevice::DisplayType type;
- sp<ISurfaceTexture> surface;
+ sp<IGraphicBufferProducer> surface;
uint32_t layerStack;
Rect viewport;
Rect frame;
const Vector<DisplayState>& displays, uint32_t flags);
virtual void bootFinished();
virtual bool authenticateSurfaceTexture(
- const sp<ISurfaceTexture>& surface) const;
+ const sp<IGraphicBufferProducer>& bufferProducer) const;
virtual sp<IDisplayEventConnection> createDisplayEventConnection();
virtual status_t captureScreen(const sp<IBinder>& display, sp<IMemoryHeap>* heap,
uint32_t* width, uint32_t* height, PixelFormat* format,
void dumpStatsLocked(const Vector<String16>& args, size_t& index,
String8& result, char* buffer, size_t SIZE) const;
void clearStatsLocked(const Vector<String16>& args, size_t& index,
- String8& result, char* buffer, size_t SIZE) const;
+ String8& result, char* buffer, size_t SIZE);
void dumpAllLocked(String8& result, char* buffer, size_t SIZE) const;
bool startDdmConnection();
static void appendSfConfigString(String8& result);
State mDrawingState;
bool mVisibleRegionsDirty;
bool mHwWorkListDirty;
+ bool mAnimCompositionPending;
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
// these are thread safe
mutable MessageQueue mEventQueue;
mutable Barrier mReadyToRunBarrier;
+ FrameTracker mAnimFrameTracker;
// protected by mDestroyedLayerLock;
mutable Mutex mDestroyedLayerLock;
--- /dev/null
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include "SurfaceFlingerConsumer.h"
+
+#include <utils/Trace.h>
+#include <utils/Errors.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter)
+{
+ ATRACE_CALL();
+ ALOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ ALOGE("updateTexImage: GLConsumer is abandoned!");
+ return NO_INIT;
+ }
+
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked();
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferQueue::BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = acquireBufferLocked(&item);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // This variant of updateTexImage does not guarantee that the
+ // texture is bound, so no need to call glBindTexture.
+ err = NO_ERROR;
+ } else {
+ ALOGE("updateTexImage: acquire failed: %s (%d)",
+ strerror(-err), err);
+ }
+ return err;
+ }
+
+
+ // We call the rejecter here, in case the caller has a reason to
+ // not accept this buffer. This is used by SurfaceFlinger to
+ // reject buffers which have the wrong size
+ int buf = item.mBuf;
+ if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
+ releaseBufferLocked(buf, EGL_NO_SYNC_KHR);
+ return NO_ERROR;
+ }
+
+ // Release the previous buffer.
+ err = releaseAndUpdateLocked(item);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (!sUseNativeFenceSync) {
+ // Bind the new buffer to the GL texture.
+ //
+ // Older devices require the "implicit" synchronization provided
+ // by glEGLImageTargetTexture2DOES, which this method calls. Newer
+ // devices will either call this in Layer::onDraw, or (if it's not
+ // a GL-composited layer) not at all.
+ err = bindTextureImageLocked();
+ }
+
+ return err;
+}
+
+status_t SurfaceFlingerConsumer::bindTextureImage()
+{
+ Mutex::Autolock lock(mMutex);
+
+ return bindTextureImageLocked();
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
--- /dev/null
+/*
+ * Copyright (C) 2012 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 ANDROID_SURFACEFLINGERCONSUMER_H
+#define ANDROID_SURFACEFLINGERCONSUMER_H
+
+#include <gui/GLConsumer.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+/*
+ * This is a thin wrapper around GLConsumer.
+ */
+class SurfaceFlingerConsumer : public GLConsumer {
+public:
+ SurfaceFlingerConsumer(GLuint tex, bool allowSynchronousMode = true,
+ GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
+ const sp<BufferQueue> &bufferQueue = 0)
+ : GLConsumer(tex, allowSynchronousMode, texTarget, useFenceSync,
+ bufferQueue)
+ {}
+
+ class BufferRejecter {
+ friend class SurfaceFlingerConsumer;
+ virtual bool reject(const sp<GraphicBuffer>& buf,
+ const BufferQueue::BufferItem& item) = 0;
+
+ protected:
+ virtual ~BufferRejecter() { }
+ };
+
+ // This version of updateTexImage() takes a functor that may be used to
+ // reject the newly acquired buffer. Unlike the GLConsumer version,
+ // this does not guarantee that the buffer has been bound to the GL
+ // texture.
+ status_t updateTexImage(BufferRejecter* rejecter);
+
+ // See GLConsumer::bindTextureImageLocked().
+ status_t bindTextureImage();
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SURFACEFLINGERCONSUMER_H
#include <utils/Errors.h>
-#include "Layer.h"
#include "SurfaceTextureLayer.h"
namespace android {
class Layer;
-// SurfaceTextureLayer is now a BufferQueue since SurfaceTexture has been
-// refactored
+/*
+ * This is a thin wrapper around BufferQueue, used by the Layer class.
+ */
class SurfaceTextureLayer : public BufferQueue
{
public:
SurfaceTextureLayer();
~SurfaceTextureLayer();
+ // After calling the superclass connect(), set or clear synchronous
+ // mode appropriately for the specified API.
virtual status_t connect(int api, QueueBufferOutput* output);
};