OSDN Git Service

am 45b0e435: Merge "Simplify OpenGL TLS access on ARM."
authorElliott Hughes <enh@google.com>
Thu, 14 Feb 2013 19:42:06 +0000 (11:42 -0800)
committerAndroid Git Automerger <android-git-automerger@android.com>
Thu, 14 Feb 2013 19:42:06 +0000 (11:42 -0800)
# Via Elliott Hughes (1) and Gerrit Code Review (1)
* commit '45b0e4350d3404bd194edc569e36ecca017c3d76':
  Simplify OpenGL TLS access on ARM.

148 files changed:
cmds/atrace/Android.mk [new file with mode: 0644]
cmds/atrace/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
cmds/atrace/NOTICE [new file with mode: 0644]
cmds/atrace/atrace.cpp [new file with mode: 0644]
cmds/bugreport/Android.mk [new file with mode: 0644]
cmds/bugreport/bugreport.c [new file with mode: 0644]
cmds/dumpstate/dumpstate.c
cmds/dumpstate/dumpstate.h
cmds/dumpstate/utils.c
cmds/flatland/Android.mk [new file with mode: 0644]
cmds/flatland/Composers.cpp [new file with mode: 0644]
cmds/flatland/Flatland.h [new file with mode: 0644]
cmds/flatland/GLHelper.cpp [new file with mode: 0644]
cmds/flatland/GLHelper.h [new file with mode: 0644]
cmds/flatland/Main.cpp [new file with mode: 0644]
cmds/flatland/README.txt [new file with mode: 0644]
cmds/flatland/Renderers.cpp [new file with mode: 0644]
cmds/installd/Android.mk [new file with mode: 0644]
cmds/installd/commands.c [new file with mode: 0644]
cmds/installd/installd.c [new file with mode: 0644]
cmds/installd/installd.h [new file with mode: 0644]
cmds/installd/tests/Android.mk [new file with mode: 0644]
cmds/installd/tests/installd_utils_test.cpp [new file with mode: 0644]
cmds/installd/utils.c [new file with mode: 0644]
cmds/ip-up-vpn/Android.mk [new file with mode: 0644]
cmds/ip-up-vpn/ip-up-vpn.c [new file with mode: 0644]
cmds/rawbu/Android.mk [new file with mode: 0644]
cmds/rawbu/NOTICE [new file with mode: 0644]
cmds/rawbu/backup.cpp [new file with mode: 0644]
cmds/screenshot/Android.mk [new file with mode: 0644]
cmds/screenshot/screenshot.c [new file with mode: 0644]
cmds/service/Android.mk [new file with mode: 0644]
cmds/service/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
cmds/service/NOTICE [new file with mode: 0644]
cmds/service/service.cpp [new file with mode: 0644]
cmds/servicemanager/Android.mk [new file with mode: 0644]
cmds/servicemanager/bctest.c [new file with mode: 0644]
cmds/servicemanager/binder.c [new file with mode: 0644]
cmds/servicemanager/binder.h [new file with mode: 0644]
cmds/servicemanager/service_manager.c [new file with mode: 0644]
include/android/configuration.h
include/android/keycodes.h
include/binder/AppOpsManager.h [new file with mode: 0644]
include/binder/IAppOpsCallback.h [new file with mode: 0644]
include/binder/IAppOpsService.h [new file with mode: 0644]
include/diskusage/dirsize.h [new file with mode: 0644]
include/gui/BufferItemConsumer.h
include/gui/BufferQueue.h
include/gui/ConsumerBase.h
include/gui/CpuConsumer.h
include/gui/DummyConsumer.h
include/gui/GLConsumer.h [moved from include/gui/SurfaceTexture.h with 78% similarity]
include/gui/GraphicBufferAlloc.h [moved from services/surfaceflinger/DisplayHardware/GraphicBufferAlloc.h with 100% similarity]
include/gui/IGraphicBufferProducer.h [moved from include/gui/ISurfaceTexture.h with 80% similarity]
include/gui/ISurface.h
include/gui/ISurfaceComposer.h
include/gui/Surface.h
include/gui/SurfaceComposerClient.h
include/gui/SurfaceTextureClient.h
include/media/drm/DrmClientAPI.h [new file with mode: 0644]
include/media/drm/DrmEngineAPI.h [new file with mode: 0644]
include/media/hardware/HDCPAPI.h
include/media/openmax/OMX_IVCommon.h
include/media/openmax/OMX_IndexExt.h [new file with mode: 0644]
include/media/openmax/OMX_VideoExt.h [new file with mode: 0644]
include/private/gui/LayerState.h
include/ui/Fence.h
include/ui/GraphicBufferAllocator.h
include/utils/BasicHashtable.h
include/utils/JenkinsHash.h [new file with mode: 0644]
include/utils/LinearAllocator.h [new file with mode: 0644]
include/utils/LruCache.h [new file with mode: 0644]
include/utils/Mutex.h
include/utils/RWLock.h
include/utils/RefBase.h
include/utils/Trace.h
include/utils/TypeHelpers.h
libs/binder/Android.mk
libs/binder/AppOpsManager.cpp [new file with mode: 0644]
libs/binder/IAppOpsCallback.cpp [new file with mode: 0644]
libs/binder/IAppOpsService.cpp [new file with mode: 0644]
libs/diskusage/Android.mk [new file with mode: 0644]
libs/diskusage/MODULE_LICENSE_APACHE2 [new file with mode: 0644]
libs/diskusage/dirsize.c [new file with mode: 0644]
libs/gui/Android.mk
libs/gui/BufferItemConsumer.cpp
libs/gui/BufferQueue.cpp
libs/gui/ConsumerBase.cpp
libs/gui/GLConsumer.cpp [moved from libs/gui/SurfaceTexture.cpp with 71% similarity]
libs/gui/GraphicBufferAlloc.cpp [moved from services/surfaceflinger/DisplayHardware/GraphicBufferAlloc.cpp with 97% similarity]
libs/gui/IGraphicBufferProducer.cpp [moved from libs/gui/ISurfaceTexture.cpp with 73% similarity]
libs/gui/ISurface.cpp
libs/gui/ISurfaceComposer.cpp
libs/gui/LayerState.cpp
libs/gui/Surface.cpp
libs/gui/SurfaceComposerClient.cpp
libs/gui/SurfaceTextureClient.cpp
libs/gui/tests/BufferQueue_test.cpp
libs/gui/tests/SurfaceTextureClient_test.cpp
libs/gui/tests/SurfaceTexture_test.cpp
libs/ui/Fence.cpp
libs/ui/GraphicBufferAllocator.cpp
libs/utils/Android.mk
libs/utils/BasicHashtable.cpp
libs/utils/CallStack.cpp
libs/utils/JenkinsHash.cpp [new file with mode: 0644]
libs/utils/LinearAllocator.cpp [new file with mode: 0644]
libs/utils/Trace.cpp
libs/utils/tests/Android.mk
libs/utils/tests/LruCache_test.cpp [new file with mode: 0644]
opengl/libs/Android.mk
opengl/libs/EGL/egl.cpp
opengl/libs/EGL/eglApi.cpp
opengl/libs/GLES_trace/DESIGN.txt
opengl/libs/GLES_trace/src/gltrace_eglapi.cpp
opengl/libs/GLES_trace/src/gltrace_fixup.cpp
opengl/libs/glestrace.h
opengl/libs/tools/genfiles
opengl/specs/EGL_ANDROID_blob_cache.txt
opengl/specs/EGL_ANDROID_framebuffer_target.txt
opengl/specs/EGL_ANDROID_image_native_buffer.txt [new file with mode: 0644]
opengl/specs/EGL_ANDROID_native_fence_sync.txt
opengl/specs/EGL_ANDROID_recordable.txt
opengl/tests/EGLTest/Android.mk
opengl/tests/EGLTest/EGL_test.cpp
opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp
opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.java
services/sensorservice/Fusion.cpp
services/surfaceflinger/Android.mk
services/surfaceflinger/DisplayDevice.cpp
services/surfaceflinger/DisplayDevice.h
services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
services/surfaceflinger/DisplayHardware/FramebufferSurface.h
services/surfaceflinger/DisplayHardware/HWComposer.cpp
services/surfaceflinger/DisplayHardware/HWComposer.h
services/surfaceflinger/FrameTracker.cpp [new file with mode: 0644]
services/surfaceflinger/FrameTracker.h [new file with mode: 0644]
services/surfaceflinger/Layer.cpp
services/surfaceflinger/Layer.h
services/surfaceflinger/LayerBase.cpp
services/surfaceflinger/LayerBase.h
services/surfaceflinger/SurfaceFlinger.cpp
services/surfaceflinger/SurfaceFlinger.h
services/surfaceflinger/SurfaceFlingerConsumer.cpp [new file with mode: 0644]
services/surfaceflinger/SurfaceFlingerConsumer.h [new file with mode: 0644]
services/surfaceflinger/SurfaceTextureLayer.cpp
services/surfaceflinger/SurfaceTextureLayer.h

diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk
new file mode 100644 (file)
index 0000000..028ca8f
--- /dev/null
@@ -0,0 +1,20 @@
+# 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)
diff --git a/cmds/atrace/MODULE_LICENSE_APACHE2 b/cmds/atrace/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/cmds/atrace/NOTICE b/cmds/atrace/NOTICE
new file mode 100644 (file)
index 0000000..c77f135
--- /dev/null
@@ -0,0 +1,190 @@
+
+   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
+
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
new file mode 100644 (file)
index 0000000..c6fda97
--- /dev/null
@@ -0,0 +1,714 @@
+/*
+ * 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;
+}
diff --git a/cmds/bugreport/Android.mk b/cmds/bugreport/Android.mk
new file mode 100644 (file)
index 0000000..f476f5e
--- /dev/null
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= bugreport.c
+
+LOCAL_MODULE:= bugreport
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/bugreport/bugreport.c b/cmds/bugreport/bugreport.c
new file mode 100644 (file)
index 0000000..4a0b511
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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;
+}
index 8718bb6..c8eafb8 100644 (file)
@@ -102,7 +102,7 @@ static void dumpstate() {
     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);
@@ -216,7 +216,7 @@ static void dumpstate() {
     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");
index 45247cd..67bbd7e 100644 (file)
 
 #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);
 
@@ -42,10 +46,13 @@ pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level);
 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);
index 8f132d5..fd390f5 100644 (file)
@@ -48,7 +48,7 @@ static const char* native_processes_to_dump[] = {
         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;
 
@@ -73,23 +73,85 @@ void for_each_pid(void (*func)(int, const char *), const char *header) {
         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;
@@ -100,7 +162,10 @@ void show_wchan(int pid, const char *name) {
         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);
diff --git a/cmds/flatland/Android.mk b/cmds/flatland/Android.mk
new file mode 100644 (file)
index 0000000..5e57f02
--- /dev/null
@@ -0,0 +1,22 @@
+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)
diff --git a/cmds/flatland/Composers.cpp b/cmds/flatland/Composers.cpp
new file mode 100644 (file)
index 0000000..8365a31
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * 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
diff --git a/cmds/flatland/Flatland.h b/cmds/flatland/Flatland.h
new file mode 100644 (file)
index 0000000..fd26ad3
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
new file mode 100644 (file)
index 0000000..3647897
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * 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;
+}
+
+}
diff --git a/cmds/flatland/GLHelper.h b/cmds/flatland/GLHelper.h
new file mode 100644 (file)
index 0000000..19fdff9
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
new file mode 100644 (file)
index 0000000..45f414e
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * 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;
+    }
+}
diff --git a/cmds/flatland/README.txt b/cmds/flatland/README.txt
new file mode 100644 (file)
index 0000000..ed47b3c
--- /dev/null
@@ -0,0 +1,74 @@
+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.
diff --git a/cmds/flatland/Renderers.cpp b/cmds/flatland/Renderers.cpp
new file mode 100644 (file)
index 0000000..f1e5488
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
new file mode 100644 (file)
index 0000000..1dd4ee5
--- /dev/null
@@ -0,0 +1,42 @@
+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)
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
new file mode 100644 (file)
index 0000000..c272e47
--- /dev/null
@@ -0,0 +1,1075 @@
+/*
+** 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;
+}
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
new file mode 100644 (file)
index 0000000..2285e79
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+** 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;
+}
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
new file mode 100644 (file)
index 0000000..efd3aa7
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+**
+** 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);
diff --git a/cmds/installd/tests/Android.mk b/cmds/installd/tests/Android.mk
new file mode 100644 (file)
index 0000000..c0192f4
--- /dev/null
@@ -0,0 +1,31 @@
+# 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)) \
+)
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
new file mode 100644 (file)
index 0000000..7cb9b37
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * 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";
+}
+
+}
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
new file mode 100644 (file)
index 0000000..625a35e
--- /dev/null
@@ -0,0 +1,1006 @@
+/*
+** 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;
+}
diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk
new file mode 100644 (file)
index 0000000..de81889
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# 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)
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
new file mode 100644 (file)
index 0000000..9fcc950
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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;
+}
diff --git a/cmds/rawbu/Android.mk b/cmds/rawbu/Android.mk
new file mode 100644 (file)
index 0000000..b580390
--- /dev/null
@@ -0,0 +1,15 @@
+# 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)
diff --git a/cmds/rawbu/NOTICE b/cmds/rawbu/NOTICE
new file mode 100644 (file)
index 0000000..c5b1efa
--- /dev/null
@@ -0,0 +1,190 @@
+
+   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
+
diff --git a/cmds/rawbu/backup.cpp b/cmds/rawbu/backup.cpp
new file mode 100644 (file)
index 0000000..70e7b57
--- /dev/null
@@ -0,0 +1,746 @@
+// 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");
+}
diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk
new file mode 100644 (file)
index 0000000..73a8e22
--- /dev/null
@@ -0,0 +1,12 @@
+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)
diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c
new file mode 100644 (file)
index 0000000..cca80c3
--- /dev/null
@@ -0,0 +1,171 @@
+#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);
+}
diff --git a/cmds/service/Android.mk b/cmds/service/Android.mk
new file mode 100644 (file)
index 0000000..275bbb2
--- /dev/null
@@ -0,0 +1,16 @@
+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)
diff --git a/cmds/service/MODULE_LICENSE_APACHE2 b/cmds/service/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/cmds/service/NOTICE b/cmds/service/NOTICE
new file mode 100644 (file)
index 0000000..c5b1efa
--- /dev/null
@@ -0,0 +1,190 @@
+
+   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
+
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
new file mode 100644 (file)
index 0000000..32db83b
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * 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;
+}
+
diff --git a/cmds/servicemanager/Android.mk b/cmds/servicemanager/Android.mk
new file mode 100644 (file)
index 0000000..8840867
--- /dev/null
@@ -0,0 +1,12 @@
+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)
diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c
new file mode 100644 (file)
index 0000000..ff5aced
--- /dev/null
@@ -0,0 +1,103 @@
+/* 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;
+}
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c
new file mode 100644 (file)
index 0000000..1985756
--- /dev/null
@@ -0,0 +1,616 @@
+/* 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;
+}
diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h
new file mode 100644 (file)
index 0000000..d8c51ef
--- /dev/null
@@ -0,0 +1,119 @@
+/* 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
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
new file mode 100644 (file)
index 0000000..3eaf1eb
--- /dev/null
@@ -0,0 +1,287 @@
+/* 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;
+}
index 0f5c14a..d5cddb3 100644 (file)
@@ -44,6 +44,7 @@ enum {
     ACONFIGURATION_DENSITY_HIGH = 240,
     ACONFIGURATION_DENSITY_XHIGH = 320,
     ACONFIGURATION_DENSITY_XXHIGH = 480,
+    ACONFIGURATION_DENSITY_XXXHIGH = 640,
     ACONFIGURATION_DENSITY_NONE = 0xffff,
 
     ACONFIGURATION_KEYBOARD_ANY  = 0x0000,
index 282e729..cf38d1a 100644 (file)
@@ -263,6 +263,8 @@ enum {
     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.
diff --git a/include/binder/AppOpsManager.h b/include/binder/AppOpsManager.h
new file mode 100644 (file)
index 0000000..f64d5da
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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
diff --git a/include/binder/IAppOpsCallback.h b/include/binder/IAppOpsCallback.h
new file mode 100644 (file)
index 0000000..7f8eb01
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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
+
diff --git a/include/binder/IAppOpsService.h b/include/binder/IAppOpsService.h
new file mode 100644 (file)
index 0000000..2883e38
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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
+
diff --git a/include/diskusage/dirsize.h b/include/diskusage/dirsize.h
new file mode 100644 (file)
index 0000000..34236c0
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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 */
index cd4df25..0840891 100644 (file)
@@ -82,7 +82,7 @@ class BufferItemConsumer: public ConsumerBase
     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(); }
 
 };
 
index 9e265ba..c59e00e 100644 (file)
@@ -21,7 +21,7 @@
 #include <EGL/eglext.h>
 
 #include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
 
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
@@ -33,7 +33,7 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-class BufferQueue : public BnSurfaceTexture {
+class BufferQueue : public BnGraphicBufferProducer {
 public:
     enum { MIN_UNDEQUEUED_BUFFERS = 2 };
     enum { NUM_BUFFER_SLOTS = 32 };
@@ -149,7 +149,7 @@ public:
     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
@@ -158,7 +158,7 @@ public:
 
     // 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.
     //
@@ -182,8 +182,9 @@ public:
            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.
@@ -264,7 +265,7 @@ public:
     // 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;
 
@@ -505,10 +506,10 @@ private:
     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.
index ee5cb29..49a764f 100644 (file)
@@ -53,7 +53,7 @@ public:
     // 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
@@ -78,7 +78,7 @@ public:
 
     // 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&);
@@ -186,7 +186,7 @@ protected:
     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.
@@ -199,7 +199,7 @@ protected:
     // 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
index 807a4b5..a08c718 100644 (file)
@@ -82,7 +82,7 @@ class CpuConsumer: public ConsumerBase
     // 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
index 7fe4d40..08e8ec8 100644 (file)
@@ -24,7 +24,7 @@ namespace android {
 
 
 // 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:
@@ -33,7 +33,7 @@ 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();
similarity index 78%
rename from include/gui/SurfaceTexture.h
rename to include/gui/GLConsumer.h
index 7c519ae..e0fa893 100644 (file)
  * 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>
 
@@ -33,6 +33,8 @@
 #include <utils/threads.h>
 
 #define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
+#define ANDROID_GRAPHICS_FRAMEAVAILABLELISTENER_JNI_ID \
+                                         "mFrameAvailableListener"
 
 namespace android {
 // ----------------------------------------------------------------------------
@@ -40,11 +42,26 @@ 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
@@ -54,7 +71,7 @@ public:
     // 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
@@ -62,31 +79,30 @@ public:
     // 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);
 
@@ -106,7 +122,7 @@ public:
     //
     // 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.
     //
@@ -159,15 +175,14 @@ public:
 
     // 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);
 
@@ -179,29 +194,29 @@ public:
     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
@@ -217,7 +232,7 @@ protected:
     // 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;
@@ -231,23 +246,33 @@ protected:
     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);
@@ -267,9 +292,7 @@ private:
 
     // 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
@@ -278,9 +301,16 @@ private:
     // 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;
 
@@ -341,9 +371,9 @@ private:
     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) {
         }
@@ -358,13 +388,13 @@ private:
         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.
@@ -377,7 +407,7 @@ private:
     // 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,
@@ -399,4 +429,4 @@ private:
 // ----------------------------------------------------------------------------
 }; // namespace android
 
-#endif // ANDROID_GUI_SURFACETEXTURE_H
+#endif // ANDROID_GUI_CONSUMER_H
similarity index 80%
rename from include/gui/ISurfaceTexture.h
rename to include/gui/IGraphicBufferProducer.h
index ae7c5c2..f230b8d 100644 (file)
@@ -14,8 +14,8 @@
  * 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>
@@ -34,10 +34,24 @@ namespace android {
 
 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,
@@ -45,8 +59,8 @@ public:
     };
 
     // 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;
@@ -164,32 +178,32 @@ public:
     // 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,
@@ -201,4 +215,4 @@ public:
 // ----------------------------------------------------------------------------
 }; // namespace android
 
-#endif // ANDROID_GUI_ISURFACETEXTURE_H
+#endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
index c0ff9fc..ce6e715 100644 (file)
@@ -31,7 +31,7 @@ namespace android {
 
 typedef int32_t    SurfaceID;
 
-class ISurfaceTexture;
+class IGraphicBufferProducer;
 
 class ISurface : public IInterface
 {
@@ -43,7 +43,7 @@ protected:
 public: 
     DECLARE_META_INTERFACE(Surface);
 
-    virtual sp<ISurfaceTexture> getSurfaceTexture() const = 0;
+    virtual sp<IGraphicBufferProducer> getSurfaceTexture() const = 0;
 };
 
 // ----------------------------------------------------------------------------
index 6500ad5..4862949 100644 (file)
@@ -39,6 +39,10 @@ class DisplayInfo;
 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);
@@ -86,10 +90,10 @@ public:
      */
     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.
index 2288fe7..8654f76 100644 (file)
@@ -37,7 +37,7 @@ namespace android {
 
 // ---------------------------------------------------------------------------
 
-class ISurfaceTexture;
+class IGraphicBufferProducer;
 class Surface;
 class SurfaceComposerClient;
 
@@ -106,6 +106,11 @@ private:
     
 // ---------------------------------------------------------------------------
 
+/*
+ * This is a small wrapper around SurfaceTextureClient.
+ *
+ * TODO: rename and/or merge with STC.
+ */
 class Surface : public SurfaceTextureClient
 {
 public:
@@ -119,7 +124,7 @@ 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);
 
@@ -130,7 +135,7 @@ public:
 
     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);
@@ -154,7 +159,7 @@ private:
     /*
      *  private stuff...
      */
-    void init(const sp<ISurfaceTexture>& surfaceTexture);
+    void init(const sp<IGraphicBufferProducer>& bufferProducer);
 
     static void cleanCachedSurfacesLocked();
 
index ae5d69a..22e6b14 100644 (file)
@@ -39,7 +39,7 @@ class DisplayInfo;
 class Composer;
 class IMemoryHeap;
 class ISurfaceComposerClient;
-class ISurfaceTexture;
+class IGraphicBufferProducer;
 class Region;
 
 // ---------------------------------------------------------------------------
@@ -122,7 +122,7 @@ public:
     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);
 
index 50fd1ba..6f09821 100644 (file)
@@ -17,8 +17,8 @@
 #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>
@@ -34,25 +34,35 @@ namespace android {
 
 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
@@ -135,11 +145,12 @@ private:
     // 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.
@@ -205,7 +216,7 @@ private:
     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;
 
diff --git a/include/media/drm/DrmClientAPI.h b/include/media/drm/DrmClientAPI.h
new file mode 100644 (file)
index 0000000..6a08933
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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_
diff --git a/include/media/drm/DrmEngineAPI.h b/include/media/drm/DrmEngineAPI.h
new file mode 100644 (file)
index 0000000..25bd34a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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_
index 23e2bdd..147448e 100644 (file)
@@ -22,6 +22,8 @@
 
 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);
 
@@ -32,7 +34,8 @@ struct HDCPModule {
         // 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,
 
@@ -46,6 +49,11 @@ struct HDCPModule {
         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
@@ -55,24 +63,48 @@ struct HDCPModule {
 
     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 &);
@@ -81,13 +113,18 @@ private:
 
 }  // 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_
index effbaae..85bf00d 100644 (file)
@@ -160,6 +160,7 @@ typedef enum OMX_COLOR_FORMATTYPE {
     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;
 
diff --git a/include/media/openmax/OMX_IndexExt.h b/include/media/openmax/OMX_IndexExt.h
new file mode 100644 (file)
index 0000000..d22df56
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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 */
diff --git a/include/media/openmax/OMX_VideoExt.h b/include/media/openmax/OMX_VideoExt.h
new file mode 100644 (file)
index 0000000..5e79b47
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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 */
index a7eb48c..5b400ca 100644 (file)
@@ -114,7 +114,7 @@ struct DisplayState {
 
     uint32_t what;
     sp<IBinder> token;
-    sp<ISurfaceTexture> surface;
+    sp<IGraphicBufferProducer> surface;
     uint32_t layerStack;
     uint32_t orientation;
     Rect viewport;
index ff6cefe..81d5d09 100644 (file)
@@ -25,6 +25,7 @@
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
 #include <utils/String8.h>
+#include <utils/Timers.h>
 
 struct ANativeWindowBuffer;
 
@@ -40,6 +41,10 @@ class Fence
 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.
@@ -69,10 +74,6 @@ public:
     // 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
@@ -85,6 +86,12 @@ public:
     // 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;
index 479cd3e..dffa788 100644 (file)
@@ -84,7 +84,6 @@ private:
     static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
     
     friend class Singleton<GraphicBufferAllocator>;
-    friend class BufferLiberatorThread;
     GraphicBufferAllocator();
     ~GraphicBufferAllocator();
     
index fdf9738..7a6c96c 100644 (file)
@@ -328,6 +328,14 @@ public:
         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);
diff --git a/include/utils/JenkinsHash.h b/include/utils/JenkinsHash.h
new file mode 100644 (file)
index 0000000..7da5dbd
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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
diff --git a/include/utils/LinearAllocator.h b/include/utils/LinearAllocator.h
new file mode 100644 (file)
index 0000000..4772bc8
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
new file mode 100644 (file)
index 0000000..302b929
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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
index de6fb39..dd201c8 100644 (file)
@@ -91,10 +91,10 @@ private:
 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);
index a5abea2..90beb5f 100644 (file)
@@ -84,10 +84,10 @@ private:
 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);
index 99f5182..0a8e10a 100644 (file)
@@ -165,10 +165,10 @@ class LightRefBase
 {
 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);
         }
index 41bce00..49578c4 100644 (file)
 
 #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;
 };
 
index 2bf33c3..13c9081 100644 (file)
@@ -291,7 +291,7 @@ ANDROID_INT64_HASH(uint64_t)
 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));
 }
 
index d449298..994d3db 100644 (file)
 
 # 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 \
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
new file mode 100644 (file)
index 0000000..5d36dd2
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp
new file mode 100644 (file)
index 0000000..e0aad23
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
new file mode 100644 (file)
index 0000000..d8922c9
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * 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
diff --git a/libs/diskusage/Android.mk b/libs/diskusage/Android.mk
new file mode 100644 (file)
index 0000000..d54f8ad
--- /dev/null
@@ -0,0 +1,24 @@
+# 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
diff --git a/libs/diskusage/MODULE_LICENSE_APACHE2 b/libs/diskusage/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/libs/diskusage/dirsize.c b/libs/diskusage/dirsize.c
new file mode 100644 (file)
index 0000000..6703783
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *
+ * 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;
+}
index d970a33..b1aeb32 100644 (file)
@@ -3,29 +3,30 @@ include $(CLEAR_VARS)
 
 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 \
index 5079883..885b4e4 100644 (file)
@@ -62,7 +62,7 @@ status_t BufferItemConsumer::acquireBuffer(BufferItem *item, bool waitForFence)
         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)",
index 086e298..d93c067 100644 (file)
@@ -29,7 +29,6 @@
 #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
@@ -147,7 +146,7 @@ status_t BufferQueue::setBufferCount(int bufferCount) {
         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) {
@@ -201,7 +200,7 @@ int BufferQueue::query(int what, int* outValue)
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
-        ST_LOGE("query: SurfaceTexture has been abandoned!");
+        ST_LOGE("query: BufferQueue has been abandoned!");
         return NO_INIT;
     }
 
@@ -234,7 +233,7 @@ status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
     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();
@@ -283,7 +282,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
         bool tryAgain = true;
         while (tryAgain) {
             if (mAbandoned) {
-                ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+                ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!");
                 return NO_INIT;
             }
 
@@ -295,7 +294,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
                 assert(mSlots[i].mBufferState == BufferSlot::FREE);
                 if (mSlots[i].mGraphicBuffer != NULL) {
                     freeBufferLocked(i);
-                    returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
+                    returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS;
                 }
             }
 
@@ -373,8 +372,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
             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);
@@ -388,20 +385,20 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
             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(
@@ -416,7 +413,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
             Mutex::Autolock lock(mMutex);
 
             if (mAbandoned) {
-                ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+                ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!");
                 return NO_INIT;
             }
 
@@ -424,7 +421,6 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>& outFence,
         }
     }
 
-
     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
@@ -450,7 +446,7 @@ status_t BufferQueue::setSynchronousMode(bool enabled) {
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
-        ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
+        ST_LOGE("setSynchronousMode: BufferQueue has been abandoned!");
         return NO_INIT;
     }
 
@@ -489,6 +485,11 @@ status_t BufferQueue::queueBuffer(int buf,
 
     input.deflate(&timestamp, &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,
@@ -499,7 +500,7 @@ status_t BufferQueue::queueBuffer(int buf,
     { // 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();
@@ -608,6 +609,9 @@ void BufferQueue::cancelBuffer(int buf, sp<Fence> fence) {
         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;
@@ -786,7 +790,7 @@ void BufferQueue::freeBufferLocked(int slot) {
         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() {
@@ -844,7 +848,7 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) {
         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();
@@ -864,8 +868,8 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,
 
     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;
index 624d7e0..fb6ba7d 100644 (file)
@@ -69,7 +69,7 @@ ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
 
     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);
@@ -84,7 +84,7 @@ ConsumerBase::~ConsumerBase() {
 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
@@ -99,7 +99,7 @@ void ConsumerBase::onFrameAvailable() {
     sp<FrameAvailableListener> listener;
     { // scope for the lock
         Mutex::Autolock lock(mMutex);
-        listener = mFrameAvailableListener;
+        listener = mFrameAvailableListener.promote();
     }
 
     if (listener != NULL) {
@@ -148,7 +148,7 @@ void ConsumerBase::abandonLocked() {
 }
 
 void ConsumerBase::setFrameAvailableListener(
-        const sp<FrameAvailableListener>& listener) {
+        const wp<FrameAvailableListener>& listener) {
     CB_LOGV("setFrameAvailableListener");
     Mutex::Autolock lock(mMutex);
     mFrameAvailableListener = listener;
@@ -228,9 +228,9 @@ status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display,
         freeBufferLocked(slot);
     }
 
-    mSlots[slot].mFence.clear();
+    mSlots[slot].mFence = Fence::NO_FENCE;
 
     return err;
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
similarity index 71%
rename from libs/gui/SurfaceTexture.cpp
rename to libs/gui/GLConsumer.cpp
index b4dfb5e..09831fb 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "SurfaceTexture"
+#define LOG_TAG "GLConsumer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
@@ -31,7 +31,7 @@
 #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>
 
@@ -39,7 +39,9 @@
 #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.
@@ -63,15 +65,13 @@ static const bool useWaitSync = true;
 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,
@@ -113,10 +113,11 @@ static float mtxRot270[16] = {
 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),
@@ -131,7 +132,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
     mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
     mAttached(true)
 {
-    ST_LOGV("SurfaceTexture");
+    ST_LOGV("GLConsumer");
 
     memcpy(mCurrentTransformMatrix, mtxIdentity,
             sizeof(mCurrentTransformMatrix));
@@ -139,13 +140,13 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
     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;
@@ -153,11 +154,54 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
     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;
@@ -165,164 +209,165 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
 
     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;
@@ -333,18 +378,18 @@ void SurfaceTexture::setReleaseFence(int fenceFd) {
     }
 }
 
-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;
     }
@@ -373,7 +418,7 @@ status_t SurfaceTexture::detachFromContext() {
 
     // 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;
@@ -390,18 +435,18 @@ status_t SurfaceTexture::detachFromContext() {
     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;
     }
@@ -425,32 +470,10 @@ status_t SurfaceTexture::attachToContext(GLuint tex) {
 
     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;
         }
     }
@@ -463,11 +486,43 @@ status_t SurfaceTexture::attachToContext(GLuint tex) {
     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) {
@@ -526,7 +581,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
     return OK;
 }
 
-bool SurfaceTexture::isExternalFormat(uint32_t format)
+bool GLConsumer::isExternalFormat(uint32_t format)
 {
     switch (format) {
     // supported YUV formats
@@ -545,19 +600,19 @@ bool SurfaceTexture::isExternalFormat(uint32_t format)
     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;
@@ -572,7 +627,7 @@ void SurfaceTexture::setFilteringEnabled(bool enabled) {
     }
 }
 
-void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+void GLConsumer::computeCurrentTransformMatrixLocked() {
     ST_LOGV("computeCurrentTransformMatrixLocked");
 
     float xform[16];
@@ -665,19 +720,19 @@ void SurfaceTexture::computeCurrentTransformMatrixLocked() {
     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[] = {
@@ -693,12 +748,12 @@ EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
     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;
@@ -734,27 +789,27 @@ Rect SurfaceTexture::getCurrentCrop() const {
     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();
@@ -769,7 +824,7 @@ status_t SurfaceTexture::doGLFenceWaitLocked() const {
         return INVALID_OPERATION;
     }
 
-    if (mCurrentFence != NULL) {
+    if (mCurrentFence->isValid()) {
         if (useWaitSync) {
             // Create an EGLSyncKHR from the current fence.
             int fenceFd = mCurrentFence->dup();
@@ -803,7 +858,7 @@ status_t SurfaceTexture::doGLFenceWaitLocked() const {
             }
         } 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;
@@ -814,12 +869,12 @@ status_t SurfaceTexture::doGLFenceWaitLocked() const {
     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;
@@ -833,42 +888,42 @@ void SurfaceTexture::freeBufferLocked(int slotIndex) {
     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,
@@ -19,7 +19,7 @@
 
 #include <ui/GraphicBuffer.h>
 
-#include "DisplayHardware/GraphicBufferAlloc.h"
+#include <gui/GraphicBufferAlloc.h>
 
 // ----------------------------------------------------------------------------
 namespace android {
similarity index 73%
rename from libs/gui/ISurfaceTexture.cpp
rename to libs/gui/IGraphicBufferProducer.cpp
index a0b1e74..54860d7 100644 (file)
@@ -25,7 +25,7 @@
 #include <binder/Parcel.h>
 #include <binder/IInterface.h>
 
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
 
 namespace android {
 // ----------------------------------------------------------------------------
@@ -43,17 +43,17 @@ enum {
 };
 
 
-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) {
@@ -71,7 +71,7 @@ public:
     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) {
@@ -84,7 +84,7 @@ public:
     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);
@@ -94,9 +94,11 @@ public:
             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());
         }
@@ -107,7 +109,7 @@ public:
     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);
@@ -121,19 +123,15 @@ public:
 
     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) {
@@ -146,7 +144,7 @@ public:
 
     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) {
@@ -158,7 +156,7 @@ public:
 
     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) {
@@ -171,7 +169,7 @@ public:
 
     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) {
@@ -182,16 +180,16 @@ public:
     }
 };
 
-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);
@@ -203,14 +201,14 @@ status_t BnSurfaceTexture::onTransact(
             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();
@@ -218,17 +216,16 @@ status_t BnSurfaceTexture::onTransact(
             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 =
@@ -239,19 +236,15 @@ status_t BnSurfaceTexture::onTransact(
             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);
@@ -260,14 +253,14 @@ status_t BnSurfaceTexture::onTransact(
             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 *>(
@@ -277,7 +270,7 @@ status_t BnSurfaceTexture::onTransact(
             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);
@@ -289,61 +282,48 @@ status_t BnSurfaceTexture::onTransact(
 
 // ----------------------------------------------------------------------------
 
-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, &timestamp,   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(&timestamp,   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;
 }
 
index c2ea183..8c25f45 100644 (file)
@@ -23,7 +23,7 @@
 #include <binder/Parcel.h>
 
 #include <gui/ISurface.h>
-#include <gui/ISurfaceTexture.h>
+#include <gui/IGraphicBufferProducer.h>
 
 namespace android {
 
@@ -37,11 +37,11 @@ public:
     {
     }
 
-    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());
     }
 };
 
index 85a9488..72b6277 100644 (file)
@@ -28,7 +28,7 @@
 #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>
 
@@ -124,7 +124,7 @@ public:
     }
 
     virtual bool authenticateSurfaceTexture(
-            const sp<ISurfaceTexture>& surfaceTexture) const
+            const sp<IGraphicBufferProducer>& bufferProducer) const
     {
         Parcel data, reply;
         int err = NO_ERROR;
@@ -135,7 +135,7 @@ public:
                     "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);
@@ -288,9 +288,9 @@ status_t BnSurfaceComposer::onTransact(
         } 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: {
index e2604f8..5e5edcd 100644 (file)
@@ -17,7 +17,7 @@
 #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 {
@@ -74,7 +74,7 @@ status_t DisplayState::write(Parcel& output) const {
 
 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();
index 1745061..51d37b3 100644 (file)
@@ -179,7 +179,7 @@ status_t SurfaceControl::writeSurfaceToParcel(
         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;
 }
@@ -205,7 +205,7 @@ Surface::Surface(const sp<SurfaceControl>& surface)
       mSurface(surface->mSurface),
       mIdentity(surface->mIdentity)
 {
-    sp<ISurfaceTexture> st;
+    sp<IGraphicBufferProducer> st;
     if (mSurface != NULL) {
         st = mSurface->getSurfaceTexture();
     }
@@ -217,9 +217,9 @@ Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
 {
     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();
     }
@@ -228,7 +228,7 @@ Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
     init(st);
 }
 
-Surface::Surface(const sp<ISurfaceTexture>& st)
+Surface::Surface(const sp<IGraphicBufferProducer>& st)
     : SurfaceTextureClient(),
       mSurface(NULL),
       mIdentity(0)
@@ -240,7 +240,7 @@ status_t Surface::writeToParcel(
         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;
@@ -249,8 +249,8 @@ status_t Surface::writeToParcel(
     } 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);
     }
@@ -275,7 +275,7 @@ sp<Surface> Surface::readFromParcel(const Parcel& data) {
     } 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) {
@@ -296,12 +296,12 @@ void Surface::cleanCachedSurfacesLocked() {
     }
 }
 
-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);
         }
 
@@ -328,7 +328,7 @@ bool Surface::isValid() {
     return getISurfaceTexture() != NULL;
 }
 
-sp<ISurfaceTexture> Surface::getSurfaceTexture() {
+sp<IGraphicBufferProducer> Surface::getSurfaceTexture() {
     return getISurfaceTexture();
 }
 
index 80dd6ee..0c6881a 100644 (file)
@@ -115,13 +115,15 @@ class Composer : public Singleton<Composer>
     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();
 
@@ -155,7 +157,8 @@ public:
     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,
@@ -166,6 +169,10 @@ public:
         Composer::getInstance().setAnimationTransactionImpl();
     }
 
+    static void openGlobalTransaction() {
+        Composer::getInstance().openGlobalTransactionImpl();
+    }
+
     static void closeGlobalTransaction(bool synchronous) {
         Composer::getInstance().closeGlobalTransactionImpl(synchronous);
     }
@@ -184,6 +191,13 @@ sp<IBinder> Composer::getBuiltInDisplay(int32_t id) {
     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());
 
@@ -193,13 +207,21 @@ void Composer::closeGlobalTransactionImpl(bool synchronous) {
 
     { // 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) {
@@ -365,10 +387,10 @@ DisplayState& Composer::getDisplayStateLocked(const sp<IBinder>& token) {
 }
 
 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;
 }
 
@@ -483,7 +505,7 @@ inline Composer& SurfaceComposerClient::getComposer() {
 // ----------------------------------------------------------------------------
 
 void SurfaceComposerClient::openGlobalTransaction() {
-    // Currently a no-op
+    Composer::openGlobalTransaction();
 }
 
 void SurfaceComposerClient::closeGlobalTransaction(bool synchronous) {
@@ -550,8 +572,8 @@ status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx,
 // ----------------------------------------------------------------------------
 
 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,
index afdbf04..5ed2e38 100644 (file)
@@ -27,7 +27,7 @@
 
 #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() {
@@ -94,12 +86,12 @@ void SurfaceTextureClient::init() {
 }
 
 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;
 }
 
@@ -205,26 +197,26 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer,
     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);
@@ -249,7 +241,7 @@ int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer,
     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;
 }
@@ -295,9 +287,9 @@ int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer, int fence
     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)  {
@@ -505,7 +497,7 @@ int SurfaceTextureClient::connect(int api) {
     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;
@@ -574,7 +566,7 @@ int SurfaceTextureClient::setBufferCount(int bufferCount)
     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) {
index 817abb4..93f8faf 100644 (file)
@@ -63,19 +63,19 @@ struct DummyConsumer : public BufferQueue::ConsumerListener {
 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));
@@ -83,7 +83,7 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
         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));
index ec14a0d..2df83a0 100644 (file)
@@ -40,8 +40,8 @@ protected:
         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()
@@ -102,7 +102,7 @@ protected:
         return sDefaultConfigAttribs;
     }
 
-    sp<SurfaceTexture> mST;
+    sp<GLConsumer> mST;
     sp<SurfaceTextureClient> mSTC;
     sp<ANativeWindow> mANW;
 
@@ -112,7 +112,7 @@ protected:
 };
 
 TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) {
-    sp<ISurfaceTexture> ist(mSTC->getISurfaceTexture());
+    sp<IGraphicBufferProducer> ist(mSTC->getISurfaceTexture());
     ASSERT_TRUE(ist != NULL);
 }
 
@@ -250,7 +250,7 @@ TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) {
 }
 
 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));
@@ -464,7 +464,7 @@ TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) {
 // 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;
@@ -480,7 +480,7 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
             return false;
         }
     public:
-        MyThread(const sp<SurfaceTexture>& mST)
+        MyThread(const sp<GLConsumer>& mST)
             : mST(mST), mBufferRetired(false) {
             ctx = eglGetCurrentContext();
             sur = eglGetCurrentSurface(EGL_DRAW);
@@ -685,8 +685,8 @@ protected:
         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());
index d9b40cf..9062f84 100644 (file)
@@ -18,7 +18,7 @@
 //#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>
@@ -202,7 +202,6 @@ protected:
             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()));
         }
@@ -228,7 +227,6 @@ protected:
             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 {
@@ -384,8 +382,8 @@ protected:
 
     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());
@@ -406,7 +404,7 @@ protected:
 
     class TextureRenderer: public RefBase {
     public:
-        TextureRenderer(GLuint texName, const sp<SurfaceTexture>& st):
+        TextureRenderer(GLuint texName, const sp<GLConsumer>& st):
                 mTexName(texName),
                 mST(st) {
         }
@@ -447,7 +445,7 @@ protected:
             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,
@@ -494,14 +492,14 @@ protected:
         }
 
         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) {
@@ -526,7 +524,7 @@ protected:
         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
@@ -575,7 +573,7 @@ protected:
         Condition mFrameCondition;
     };
 
-    sp<SurfaceTexture> mST;
+    sp<GLConsumer> mST;
     sp<SurfaceTextureClient> mSTC;
     sp<ANativeWindow> mANW;
     sp<TextureRenderer> mTextureRenderer;
@@ -1070,7 +1068,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) {
     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.
@@ -1123,12 +1121,12 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) {
     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());
 
@@ -1136,7 +1134,7 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) {
 }
 
 
-// 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) {
@@ -1581,7 +1579,7 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) {
     // 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());
@@ -1615,7 +1613,7 @@ TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
         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();
 
@@ -1847,7 +1845,7 @@ TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedGLFilledBuffer) {
  * 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.
  *
@@ -1914,13 +1912,13 @@ protected:
     // 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),
@@ -1951,7 +1949,7 @@ protected:
             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");
index d214b97..b9e0f00 100644 (file)
@@ -18,6 +18,9 @@
 #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>
@@ -26,7 +29,7 @@
 
 namespace android {
 
-const sp<Fence> Fence::NO_FENCE = sp<Fence>();
+const sp<Fence> Fence::NO_FENCE = sp<Fence>(new Fence);
 
 Fence::Fence() :
     mFenceFd(-1) {
@@ -68,7 +71,19 @@ status_t Fence::waitForever(unsigned int warningTimeout, const char* logname) {
 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)",
@@ -80,10 +95,34 @@ sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
 }
 
 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 {
@@ -91,22 +130,24 @@ 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) {
@@ -114,7 +155,10 @@ status_t Fence::unflatten(void const* buffer, size_t size, int fds[],
         return INVALID_OPERATION;
     }
 
-    mFenceFd = fds[0];
+    if (count == 1) {
+        mFenceFd = fds[0];
+    }
+
     return NO_ERROR;
 }
 
index fb43410..ff550d9 100644 (file)
@@ -90,105 +90,6 @@ void GraphicBufferAllocator::dumpToSystemLog()
     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)
 {
@@ -199,24 +100,13 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma
         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);
@@ -239,11 +129,21 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma
     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;
 }
 
 // ---------------------------------------------------------------------------
index c9f8fd4..cbfe7bd 100644 (file)
@@ -25,6 +25,8 @@ commonSources:= \
        Debug.cpp \
        FileMap.cpp \
        Flattenable.cpp \
+       JenkinsHash.cpp \
+       LinearAllocator.cpp \
        LinearTransform.cpp \
        Log.cpp \
        PropertyMap.cpp \
@@ -111,6 +113,10 @@ ifeq ($(TARGET_OS),linux)
 LOCAL_LDLIBS += -lrt -ldl
 endif
 
+ifeq ($(TARGET_ARCH),mips)
+LOCAL_CFLAGS += -DALIGN_DOUBLE
+endif
+
 LOCAL_C_INCLUDES += \
                bionic/libc/private \
                external/zlib
index fb8ec9f..fd51b7b 100644 (file)
@@ -80,7 +80,7 @@ void BasicHashtableImpl::clear() {
             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;
                 }
index 18fd84f..66fabeb 100644 (file)
@@ -118,7 +118,9 @@ String8 CallStack::toString(const char* prefix) const {
         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");
     }
diff --git a/libs/utils/JenkinsHash.cpp b/libs/utils/JenkinsHash.cpp
new file mode 100644 (file)
index 0000000..52c9bb7
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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;
+}
+
+}
+
diff --git a/libs/utils/LinearAllocator.cpp b/libs/utils/LinearAllocator.cpp
new file mode 100644 (file)
index 0000000..a07a291
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * 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
index f5aaea3..36fd802 100644 (file)
  * 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
index 5b2b5b1..a2ca9c8 100644 (file)
@@ -7,6 +7,7 @@ test_src_files := \
     BasicHashtable_test.cpp \
     BlobCache_test.cpp \
     Looper_test.cpp \
+    LruCache_test.cpp \
     String8_test.cpp \
     Unicode_test.cpp \
     Vector_test.cpp \
diff --git a/libs/utils/tests/LruCache_test.cpp b/libs/utils/tests/LruCache_test.cpp
new file mode 100644 (file)
index 0000000..e573952
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * 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);
+}
+
+}
index d025ae8..ae17096 100644 (file)
@@ -4,6 +4,20 @@ LOCAL_PATH:= $(call my-dir)
 # 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:=             \
@@ -56,24 +70,10 @@ ifneq ($(MAX_EGL_CACHE_SIZE),)
   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
index 0d4bed5..bf2477a 100644 (file)
@@ -84,13 +84,20 @@ static int sEGLApplicationTraceLevel;
 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);
 }
@@ -122,36 +129,36 @@ void initEglTraceLevel() {
 }
 
 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);
+        }
     }
 }
 
@@ -165,10 +172,11 @@ void setGLHooksThreadSpecific(gl_hooks_t const *value) {
     } 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);
     }
 }
@@ -186,9 +194,12 @@ void setGLTraceLevel(int level) {
  * 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
@@ -229,7 +240,6 @@ static void early_egl_init(void)
 #if EGL_TRACE
     pthread_key_create(&gGLTraceKey, NULL);
     initEglTraceLevel();
-    initEglDebugLevel();
 #endif
     uint32_t addr = (uint32_t)((void*)gl_no_context);
     android_memset32(
index 065faf2..7a84651 100644 (file)
@@ -97,7 +97,8 @@ namespace android {
 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;
 
@@ -465,7 +466,7 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
             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;
@@ -578,7 +579,7 @@ EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
             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();
@@ -861,8 +862,31 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
         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);
@@ -1081,7 +1105,7 @@ EGLBoolean eglReleaseThread(void)
 
     egl_tls_t::clearTLS();
 #if EGL_TRACE
-    if (gEGLDebugLevel > 0)
+    if (getEGLDebugLevel() > 0)
         GLTrace_eglReleaseThread();
 #endif
     return EGL_TRUE;
index a189e1d..72a2e15 100644 (file)
@@ -9,9 +9,6 @@ Code Runtime Behavior:
     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.
@@ -30,6 +27,37 @@ Code Runtime Behavior:
     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
index 9698bf9..512d562 100644 (file)
@@ -33,6 +33,9 @@ using gltrace::GLTraceState;
 using gltrace::GLTraceContext;
 using gltrace::TCPStream;
 
+static pthread_mutex_t sGlTraceStateLock = PTHREAD_MUTEX_INITIALIZER;
+
+static int sGlTraceInProgress;
 static GLTraceState *sGLTraceState;
 static pthread_t sReceiveThreadId;
 
@@ -105,33 +108,66 @@ static void *commandReceiveTask(void *arg) {
     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
@@ -139,8 +175,19 @@ void GLTrace_eglCreateContext(int version, EGLContext c) {
 }
 
 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);
 
index 36ae314..a6c024b 100644 (file)
@@ -236,11 +236,9 @@ void fixup_glShaderSource(GLMessage *glmsg, void *pointersToFixup[]) {
     ::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);
index a08f97b..868b18d 100644 (file)
@@ -30,7 +30,7 @@ void GLTrace_eglReleaseThread();
 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. */
index 50bbf08..9a2038e 100755 (executable)
 # 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 \
index 61f45d3..e984694 100644 (file)
@@ -16,15 +16,15 @@ Contact
 
 Status
 
-    Draft.
+    Complete
 
 Version
 
-    Version 1, April 22, 2011
+    Version 3, December 13, 2012
 
 Number
 
-    EGL Extension #XXX
+    EGL Extension #48
 
 Dependencies
 
@@ -88,8 +88,8 @@ New Types
 New Procedures and Functions
 
     void eglSetBlobCacheFuncsANDROID(EGLDisplay dpy,
-                                     EGLSetBlobFunc set,
-                                     EGLGetBlobFunc get);
+                                     EGLSetBlobFuncANDROID set,
+                                     EGLGetBlobFuncANDROID get);
 
 New Tokens
 
@@ -200,6 +200,9 @@ Issues
 
 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.
index 273414c..a15dfa8 100644 (file)
@@ -16,7 +16,7 @@ Contact
 
 Status
 
-    Draft.
+    Complete
 
 Version
 
@@ -24,7 +24,7 @@ Version
 
 Number
 
-    EGL Extension #XXX
+    EGL Extension #47
 
 Dependencies
 
diff --git a/opengl/specs/EGL_ANDROID_image_native_buffer.txt b/opengl/specs/EGL_ANDROID_image_native_buffer.txt
new file mode 100644 (file)
index 0000000..7392d4f
--- /dev/null
@@ -0,0 +1,108 @@
+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.
index ee05b40..d72edd7 100644 (file)
@@ -16,7 +16,7 @@ Contact
 
 Status
 
-    Draft.
+    Complete
 
 Version
 
@@ -24,7 +24,7 @@ Version
 
 Number
 
-    EGL Extension #XXX
+    EGL Extension #50
 
 Dependencies
 
index 8dbd26f..d21094e 100644 (file)
@@ -16,15 +16,15 @@ Contact
 
 Status
 
-    Draft.
+    Complete
 
 Version
 
-    Version 1, July 8, 2011
+    Version 2, July 15, 2011
 
 Number
 
-    EGL Extension #XXX
+    EGL Extension #51
 
 Dependencies
 
index d820875..1a9ee5c 100644 (file)
@@ -30,6 +30,9 @@ LOCAL_C_INCLUDES := \
     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
index d511cae..ec433fb 100644 (file)
@@ -104,7 +104,7 @@ TEST_F(EGLTest, EGLTerminateSucceedsWithRemainingObjects) {
     // 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,
index 7904ac7..290d102 100644 (file)
@@ -1,4 +1,4 @@
-**
+/*
 ** Copyright 2012, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@
 #include <EGL/egl.h>
 
 #include <gui/Surface.h>
-#include <gui/SurfaceTexture.h>
+#include <gui/GLConsumer.h>
 #include <gui/SurfaceTextureClient.h>
 
 #include <ui/ANativeObjectBase.h>
index 610cde5..a3d000a 100644 (file)
@@ -90,7 +90,7 @@ android_eglCreateWindowSurfaceTexture
     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;
@@ -111,8 +111,8 @@ not_valid_surface:
         _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;
index e42334e..5d088bd 100644 (file)
@@ -29,6 +29,8 @@
         } else if (win instanceof SurfaceHolder) {
             SurfaceHolder holder = (SurfaceHolder)win;
             sur = holder.getSurface();
+        } else if (win instanceof Surface) {
+            sur = (Surface) win;
         }
 
         EGLSurface surface;
@@ -40,7 +42,7 @@
         } 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.");
         }
 
index b88a647..93d6127 100644 (file)
@@ -201,15 +201,15 @@ void Fusion::initFusion(const vec4_t& q, float dT)
     // 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
@@ -220,6 +220,22 @@ void Fusion::initFusion(const vec4_t& q, float dT)
     // 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 {
@@ -357,6 +373,11 @@ mat33_t Fusion::getRotationMatrix() 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;
@@ -368,10 +389,18 @@ void Fusion::predict(const vec3_t& w, float dT) {
     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 |
@@ -392,13 +421,26 @@ void Fusion::predict(const vec3_t& w, float dT) {
     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();
@@ -425,7 +467,12 @@ void Fusion::update(const vec3_t& z, const vec3_t& Bi, float sigma) {
     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];
index 5a57697..b4b19b4 100644 (file)
@@ -5,17 +5,18 @@ LOCAL_SRC_FILES:= \
     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                           \
     
index 69b9c34..19169a5 100644 (file)
@@ -427,6 +427,11 @@ void DisplayDevice::updateGeometryTransform() {
         const uint8_t type = mGlobalTransform.getType();
         mNeedsFiltering = (!mGlobalTransform.preserveRects() ||
                 (type >= Transform::SCALE));
+
+        mScissor = mGlobalTransform.transform(mViewport);
+        if (mScissor.isEmpty()) {
+            mScissor.set(getBounds());
+        }
     }
 }
 
@@ -436,7 +441,7 @@ void DisplayDevice::dump(String8& result, char* buffer, size_t SIZE) const {
         "+ 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(),
@@ -444,6 +449,7 @@ void DisplayDevice::dump(String8& result, char* buffer, size_t SIZE) const {
         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]);
index d6da422..bb6eb70 100644 (file)
@@ -107,6 +107,7 @@ public:
     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; }
@@ -200,8 +201,12 @@ private:
 
     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;
 };
index 6c86a53..cccb29b 100644 (file)
 
 #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
index 6336345..b61b7f5 100644 (file)
@@ -46,7 +46,7 @@ public:
     // 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);
 
index 068fdcd..7a24d4c 100644 (file)
@@ -409,6 +409,11 @@ nsecs_t HWComposer::getRefreshTimestamp(int disp) const {
     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;
 }
@@ -494,7 +499,7 @@ status_t HWComposer::createWorkList(int32_t id, size_t numLayers) {
         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;
@@ -534,7 +539,7 @@ status_t HWComposer::setFramebufferTarget(int32_t id,
     }
 
     int acquireFenceFd = -1;
-    if (acquireFence != NULL) {
+    if (acquireFence->isValid()) {
         acquireFenceFd = acquireFence->dup();
     }
 
@@ -653,9 +658,11 @@ status_t HWComposer::commit() {
 
         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;
@@ -718,9 +725,7 @@ int HWComposer::fbPost(int32_t id,
     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);
     }
 }
@@ -775,9 +780,10 @@ private:
  * 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;
@@ -796,17 +802,28 @@ public:
     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) {
@@ -869,7 +886,7 @@ HWComposer::LayerListIterator HWComposer::getLayerIterator(int32_t id, size_t in
     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);
 }
 
 /*
index 7c67407..05f7784 100644 (file)
 
 #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,
@@ -141,7 +143,6 @@ public:
         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;
@@ -151,6 +152,7 @@ public:
         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;
     };
 
@@ -233,6 +235,7 @@ public:
     // 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;
@@ -306,6 +309,10 @@ private:
         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;
     };
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
new file mode 100644 (file)
index 0000000..9b55d44
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
new file mode 100644 (file)
index 0000000..3d122c4
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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
index 7edbdc5..1401154 100644 (file)
@@ -59,7 +59,6 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client)
         mCurrentOpacity(true),
         mRefreshPending(false),
         mFrameLatencyNeeded(false),
-        mFrameLatencyOffset(0),
         mFormat(PIXEL_FORMAT_NONE),
         mGLExtensions(GLExtensions::getInstance()),
         mOpaqueLayer(true),
@@ -74,7 +73,7 @@ void Layer::onLayerDisplayed(const sp<const DisplayDevice>& hw,
         HWComposer::HWCLayerInterface* layer) {
     LayerBaseClient::onLayerDisplayed(hw, layer);
     if (layer) {
-        mSurfaceTexture->setReleaseFence(layer->getAndResetReleaseFenceFd());
+        mSurfaceFlingerConsumer->setReleaseFence(layer->getAndResetReleaseFenceFd());
     }
 }
 
@@ -82,32 +81,20 @@ void Layer::onFirstRef()
 {
     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());
@@ -119,7 +106,7 @@ Layer::~Layer()
     mFlinger->deleteTextureAsync(mTextureName);
 }
 
-void Layer::onFrameQueued() {
+void Layer::onFrameAvailable() {
     android_atomic_inc(&mQueuedFrames);
     mFlinger->signalLayerUpdate();
 }
@@ -128,23 +115,39 @@ void Layer::onFrameQueued() {
 // 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;
         }
@@ -159,7 +162,7 @@ sp<ISurface> Layer::createSurface()
 
 wp<IBinder> Layer::getSurfaceTextureBinder() const
 {
-    return mSurfaceTexture->getBufferQueue()->asBinder();
+    return mSurfaceFlingerConsumer->getBufferQueue()->asBinder();
 }
 
 status_t Layer::setBuffers( uint32_t w, uint32_t h,
@@ -190,15 +193,15 @@ 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;
@@ -215,7 +218,7 @@ Rect Layer::computeBufferCrop() const {
     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;
@@ -248,16 +251,13 @@ void Layer::setGeometry(
     // 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
@@ -297,8 +297,8 @@ void Layer::setAcquireFence(const sp<const DisplayDevice>& hw,
     // 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);
@@ -340,9 +340,11 @@ void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const
         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.
     }
@@ -355,8 +357,8 @@ void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const
 
         // 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);
@@ -475,7 +477,7 @@ uint32_t Layer::doTransaction(uint32_t flags)
 
         // 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);
     }
 
@@ -520,12 +522,30 @@ bool Layer::onPreComposition() {
 
 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;
     }
 }
@@ -559,7 +579,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
             mFlinger->signalLayerUpdate();
         }
 
-        struct Reject : public SurfaceTexture::BufferRejecter {
+        struct Reject : public SurfaceFlingerConsumer::BufferRejecter {
             Layer::State& front;
             Layer::State& current;
             bool& recomputeVisibleRegions;
@@ -644,14 +664,14 @@ Region Layer::latchBuffer(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;
@@ -665,9 +685,9 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions)
             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))
@@ -726,35 +746,21 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const
 
     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
@@ -780,7 +786,7 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const {
             orientation = 0;
         }
     }
-    mSurfaceTexture->setTransformHint(orientation);
+    mSurfaceFlingerConsumer->setTransformHint(orientation);
 }
 
 // ---------------------------------------------------------------------------
index c5eb26b..a82767b 100644 (file)
@@ -20,8 +20,6 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <gui/SurfaceTexture.h>
-
 #include <utils/Timers.h>
 
 #include <ui/GraphicBuffer.h>
@@ -34,6 +32,8 @@
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 
+#include "SurfaceFlingerConsumer.h"
+#include "FrameTracker.h"
 #include "LayerBase.h"
 #include "SurfaceTextureLayer.h"
 #include "Transform.h"
@@ -47,10 +47,19 @@ class GLExtensions;
 
 // ---------------------------------------------------------------------------
 
-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"; }
@@ -90,7 +99,7 @@ public:
     // 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;
 
@@ -101,22 +110,26 @@ protected:
     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;
@@ -126,17 +139,6 @@ private:
     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;
index 9b03c74..54c51bb 100644 (file)
@@ -283,7 +283,7 @@ void LayerBase::setGeometry(
         layer.setTransform(finalTransform);
     }
 
-    if (!isOpaque()) {
+    if (!isOpaque() || s.alpha != 0xFF) {
         layer.setBlending(mPremultipliedAlpha ?
                 HWC_BLENDING_PREMULT :
                 HWC_BLENDING_COVERAGE);
@@ -300,7 +300,6 @@ void LayerBase::setGeometry(
 
 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.
@@ -506,7 +505,7 @@ LayerBaseClient::~LayerBaseClient()
 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)
index 4d5a5b0..47473e7 100644 (file)
@@ -49,7 +49,19 @@ class SurfaceFlinger;
 
 // ---------------------------------------------------------------------------
 
-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;
 
@@ -243,8 +255,8 @@ public:
     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 { }
 
@@ -308,16 +320,27 @@ private:
 
 // ---------------------------------------------------------------------------
 
+/*
+ * 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); }
@@ -330,6 +353,10 @@ protected:
     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;
@@ -344,8 +371,13 @@ private:
     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;
index 9afa4c1..caabf1d 100644 (file)
@@ -40,6 +40,7 @@
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/SurfaceTextureClient.h>
+#include <gui/GraphicBufferAlloc.h>
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
@@ -65,7 +66,6 @@
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/FramebufferSurface.h"
-#include "DisplayHardware/GraphicBufferAlloc.h"
 #include "DisplayHardware/HWComposer.h"
 
 
@@ -93,6 +93,7 @@ SurfaceFlinger::SurfaceFlinger()
         mBootTime(systemTime()),
         mVisibleRegionsDirty(false),
         mHwWorkListDirty(false),
+        mAnimCompositionPending(false),
         mDebugRegion(0),
         mDebugDDMS(0),
         mDebugDisableHWC(0),
@@ -503,7 +504,7 @@ status_t SurfaceFlinger::readyToRun()
 
             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) {
@@ -571,17 +572,22 @@ uint32_t SurfaceFlinger::getMaxViewportDims() const {
 // ----------------------------------------------------------------------------
 
 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;
@@ -590,7 +596,7 @@ bool SurfaceFlinger::authenticateSurfaceTexture(
     }
 
     // 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
@@ -831,10 +837,10 @@ void SurfaceFlinger::doDebugFlashRegions()
                 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);
@@ -880,6 +886,22 @@ void SurfaceFlinger::postComposition()
     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() {
@@ -1172,7 +1194,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
                         // 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) {
@@ -1296,6 +1318,10 @@ void SurfaceFlinger::commitTransaction()
         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;
@@ -1537,7 +1563,20 @@ void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const
             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
@@ -1545,13 +1584,12 @@ void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const
             }
         }
 
-        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
@@ -1559,9 +1597,6 @@ void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const
                 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);
             }
@@ -1637,10 +1672,10 @@ void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& hw,
     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);
@@ -2250,22 +2285,26 @@ void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index
         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()) {
@@ -2281,6 +2320,8 @@ void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& inde
             layer->clearStats();
         }
     }
+
+    mAnimFrameTracker.clear();
 }
 
 /*static*/ void SurfaceFlinger::appendSfConfigString(String8& result)
index 1b549e4..72dd652 100644 (file)
@@ -45,8 +45,9 @@
 #include <private/gui/LayerState.h>
 
 #include "Barrier.h"
-#include "MessageQueue.h"
 #include "DisplayDevice.h"
+#include "FrameTracker.h"
+#include "MessageQueue.h"
 
 #include "DisplayHardware/HWComposer.h"
 
@@ -163,7 +164,7 @@ private:
         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;
@@ -195,7 +196,7 @@ private:
             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,
@@ -390,7 +391,7 @@ private:
     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);
@@ -432,6 +433,7 @@ private:
     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
@@ -451,6 +453,7 @@ private:
     // these are thread safe
     mutable MessageQueue mEventQueue;
     mutable Barrier mReadyToRunBarrier;
+    FrameTracker mAnimFrameTracker;
 
     // protected by mDestroyedLayerLock;
     mutable Mutex mDestroyedLayerLock;
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
new file mode 100644 (file)
index 0000000..e427072
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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
+
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h
new file mode 100644 (file)
index 0000000..22eec81
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
index 0b638b4..395c8c8 100644 (file)
@@ -20,7 +20,6 @@
 
 #include <utils/Errors.h>
 
-#include "Layer.h"
 #include "SurfaceTextureLayer.h"
 
 namespace android {
index e892ea0..a75ccf4 100644 (file)
@@ -29,14 +29,17 @@ 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);
 };