summaryrefslogtreecommitdiffstats
path: root/cmds
diff options
context:
space:
mode:
Diffstat (limited to 'cmds')
-rw-r--r--cmds/atrace/Android.mk20
-rw-r--r--cmds/atrace/MODULE_LICENSE_APACHE20
-rw-r--r--cmds/atrace/NOTICE190
-rw-r--r--cmds/atrace/atrace.cpp891
-rw-r--r--cmds/bugreport/Android.mk10
-rw-r--r--cmds/bugreport/bugreport.c56
-rw-r--r--cmds/dumpstate/dumpstate.c35
-rw-r--r--cmds/dumpstate/dumpstate.h11
-rw-r--r--cmds/dumpstate/utils.c79
-rw-r--r--cmds/flatland/Android.mk22
-rw-r--r--cmds/flatland/Composers.cpp282
-rw-r--r--cmds/flatland/Flatland.h71
-rw-r--r--cmds/flatland/GLHelper.cpp459
-rw-r--r--cmds/flatland/GLHelper.h95
-rw-r--r--cmds/flatland/Main.cpp716
-rw-r--r--cmds/flatland/README.txt74
-rw-r--r--cmds/flatland/Renderers.cpp197
-rw-r--r--cmds/installd/Android.mk42
-rw-r--r--cmds/installd/commands.c1075
-rw-r--r--cmds/installd/installd.c586
-rw-r--r--cmds/installd/installd.h212
-rw-r--r--cmds/installd/tests/Android.mk31
-rw-r--r--cmds/installd/tests/installd_utils_test.cpp437
-rw-r--r--cmds/installd/utils.c1006
-rw-r--r--cmds/ip-up-vpn/Android.mk26
-rw-r--r--cmds/ip-up-vpn/ip-up-vpn.c153
-rw-r--r--cmds/rawbu/Android.mk15
-rw-r--r--cmds/rawbu/NOTICE190
-rw-r--r--cmds/rawbu/backup.cpp746
-rw-r--r--cmds/screenshot/Android.mk12
-rw-r--r--cmds/screenshot/screenshot.c171
-rw-r--r--cmds/service/Android.mk16
-rw-r--r--cmds/service/MODULE_LICENSE_APACHE20
-rw-r--r--cmds/service/NOTICE190
-rw-r--r--cmds/service/service.cpp275
-rw-r--r--cmds/servicemanager/Android.mk12
-rw-r--r--cmds/servicemanager/bctest.c103
-rw-r--r--cmds/servicemanager/binder.c616
-rw-r--r--cmds/servicemanager/binder.h119
-rw-r--r--cmds/servicemanager/service_manager.c287
-rw-r--r--cmds/surfaceflinger/main_surfaceflinger.cpp2
41 files changed, 9508 insertions, 22 deletions
diff --git a/cmds/atrace/Android.mk b/cmds/atrace/Android.mk
new file mode 100644
index 0000000..028ca8f
--- /dev/null
+++ b/cmds/atrace/Android.mk
@@ -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
index 0000000..e69de29
--- /dev/null
+++ b/cmds/atrace/MODULE_LICENSE_APACHE2
diff --git a/cmds/atrace/NOTICE b/cmds/atrace/NOTICE
new file mode 100644
index 0000000..c77f135
--- /dev/null
+++ b/cmds/atrace/NOTICE
@@ -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
index 0000000..76ba81f
--- /dev/null
+++ b/cmds/atrace/atrace.cpp
@@ -0,0 +1,891 @@
+/*
+ * 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";
+const char* k_traceAppCmdlineProperty = "debug.atrace.app_cmdlines";
+
+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, { } },
+ { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } },
+ { "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;
+static const char* g_kernelTraceFuncs = NULL;
+static const char* g_debugAppCmdLine = "";
+
+/* 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_currentTracerPath =
+ "/sys/kernel/debug/tracing/current_tracer";
+
+static const char* k_printTgidPath =
+ "/sys/kernel/debug/tracing/options/print-tgid";
+
+static const char* k_funcgraphAbsTimePath =
+ "/sys/kernel/debug/tracing/options/funcgraph-abstime";
+
+static const char* k_funcgraphCpuPath =
+ "/sys/kernel/debug/tracing/options/funcgraph-cpu";
+
+static const char* k_funcgraphProcPath =
+ "/sys/kernel/debug/tracing/options/funcgraph-proc";
+
+static const char* k_funcgraphFlatPath =
+ "/sys/kernel/debug/tracing/options/funcgraph-flat";
+
+static const char* k_funcgraphDurationPath =
+ "/sys/kernel/debug/tracing/options/funcgraph-duration";
+
+static const char* k_ftraceFilterPath =
+ "/sys/kernel/debug/tracing/set_ftrace_filter";
+
+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;
+}
+
+// Truncate a file.
+static bool truncateFile(const char* path)
+{
+ // This uses creat rather than truncate because some of the debug kernel
+ // device nodes (e.g. k_ftraceFilterPath) currently aren't changed by
+ // calls to truncate, but they are cleared by calls to creat.
+ int traceFD = creat(path, 0);
+ if (traceFD == -1) {
+ fprintf(stderr, "error truncating %s: %s (%d)\n", path,
+ strerror(errno), errno);
+ return false;
+ }
+
+ close(traceFD);
+
+ return true;
+}
+
+static bool _writeStr(const char* filename, const char* str, int flags)
+{
+ int fd = open(filename, flags);
+ 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;
+}
+
+// Write a string to a file, returning true if the write was successful.
+static bool writeStr(const char* filename, const char* str)
+{
+ return _writeStr(filename, str, O_WRONLY);
+}
+
+// Append a string to a file, returning true if the write was successful.
+static bool appendStr(const char* filename, const char* str)
+{
+ return _writeStr(filename, str, O_APPEND|O_WRONLY);
+}
+
+// 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()
+{
+ return truncateFile(k_tracePath);
+}
+
+// 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");
+}
+
+static bool setPrintTgidEnableIfPresent(bool enable)
+{
+ if (fileExists(k_printTgidPath)) {
+ return setKernelOptionEnable(k_printTgidPath, enable);
+ }
+ return true;
+}
+
+// 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 true;
+}
+
+// Set the system property that indicates which apps should perform
+// application-level tracing.
+static bool setAppCmdlineProperty(const char* cmdline)
+{
+ if (property_set(k_traceAppCmdlineProperty, cmdline) < 0) {
+ fprintf(stderr, "error setting trace app system property\n");
+ return false;
+ }
+ return true;
+}
+
+// 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;
+}
+
+// Verify that the comma separated list of functions are being traced by the
+// kernel.
+static bool verifyKernelTraceFuncs(const char* funcs)
+{
+ int fd = open(k_ftraceFilterPath, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "error opening %s: %s (%d)\n", k_ftraceFilterPath,
+ strerror(errno), errno);
+ return false;
+ }
+
+ char buf[4097];
+ ssize_t n = read(fd, buf, 4096);
+ close(fd);
+ if (n == -1) {
+ fprintf(stderr, "error reading %s: %s (%d)\n", k_ftraceFilterPath,
+ strerror(errno), errno);
+ return false;
+ }
+
+ buf[n] = '\0';
+ String8 funcList = String8::format("\n%s", buf);
+
+ // Make sure that every function listed in funcs is in the list we just
+ // read from the kernel.
+ bool ok = true;
+ char* myFuncs = strdup(funcs);
+ char* func = strtok(myFuncs, ",");
+ while (func) {
+ String8 fancyFunc = String8::format("\n%s\n", func);
+ bool found = funcList.find(fancyFunc.string(), 0) >= 0;
+ if (!found || func[0] == '\0') {
+ fprintf(stderr, "error: \"%s\" is not a valid kernel function "
+ "to trace.\n", func);
+ ok = false;
+ }
+ func = strtok(NULL, ",");
+ }
+ free(myFuncs);
+
+ return ok;
+}
+
+// Set the comma separated list of functions that the kernel is to trace.
+static bool setKernelTraceFuncs(const char* funcs)
+{
+ bool ok = true;
+
+ if (funcs == NULL || funcs[0] == '\0') {
+ // Disable kernel function tracing.
+ if (fileIsWritable(k_currentTracerPath)) {
+ ok &= writeStr(k_currentTracerPath, "nop");
+ }
+ if (fileIsWritable(k_ftraceFilterPath)) {
+ ok &= truncateFile(k_ftraceFilterPath);
+ }
+ } else {
+ // Enable kernel function tracing.
+ ok &= writeStr(k_currentTracerPath, "function_graph");
+ ok &= setKernelOptionEnable(k_funcgraphAbsTimePath, true);
+ ok &= setKernelOptionEnable(k_funcgraphCpuPath, true);
+ ok &= setKernelOptionEnable(k_funcgraphProcPath, true);
+ ok &= setKernelOptionEnable(k_funcgraphFlatPath, true);
+
+ // Set the requested filter functions.
+ ok &= truncateFile(k_ftraceFilterPath);
+ char* myFuncs = strdup(funcs);
+ char* func = strtok(myFuncs, ",");
+ while (func) {
+ ok &= appendStr(k_ftraceFilterPath, func);
+ func = strtok(NULL, ",");
+ }
+ free(myFuncs);
+
+ // Verify that the set functions are being traced.
+ if (ok) {
+ ok &= verifyKernelTraceFuncs(funcs);
+ }
+ }
+
+ return ok;
+}
+
+// Set all the kernel tracing settings to the desired state for this trace
+// capture.
+static bool setUpTrace()
+{
+ bool ok = true;
+
+ // Set up the tracing options.
+ ok &= setTraceOverwriteEnable(g_traceOverwrite);
+ ok &= setTraceBufferSizeKB(g_traceBufferSizeKB);
+ ok &= setGlobalClockEnable(true);
+ ok &= setPrintTgidEnableIfPresent(true);
+ ok &= setKernelTraceFuncs(g_kernelTraceFuncs);
+
+ // 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);
+ ok &= setAppCmdlineProperty(g_debugAppCmdLine);
+ ok &= pokeBinderServices();
+
+ // 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;
+ }
+ }
+ }
+ }
+ }
+
+ return ok;
+}
+
+// Reset all the kernel tracing settings to their default state.
+static void cleanUpTrace()
+{
+ // Disable all tracing that we're able to.
+ disableKernelTraceEvents();
+
+ // Reset the system properties.
+ setTagsProperty(0);
+ setAppCmdlineProperty("");
+ pokeBinderServices();
+
+ // Set the options back to their defaults.
+ setTraceOverwriteEnable(true);
+ setTraceBufferSizeKB(1);
+ setGlobalClockEnable(false);
+ setPrintTgidEnableIfPresent(false);
+ setKernelTraceFuncs(NULL);
+}
+
+
+// Enable tracing in the kernel.
+static bool startTrace()
+{
+ return setTracingEnabled(true);
+}
+
+// Disable tracing in the kernel.
+static void stopTrace()
+{
+ setTracingEnabled(false);
+}
+
+// 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"
+ " -a appname enable app-level tracing for a comma "
+ "separated list of cmdlines\n"
+ " -b N use a trace buffer size of N KB\n"
+ " -c trace into a circular buffer\n"
+ " -k fname,... trace the listed kernel functions\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, "a:b:ck:ns: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 'a':
+ g_debugAppCmdLine = optarg;
+ break;
+
+ case 'b':
+ g_traceBufferSizeKB = atoi(optarg);
+ break;
+
+ case 'c':
+ g_traceOverwrite = true;
+ break;
+
+ case 'k':
+ g_kernelTraceFuncs = optarg;
+ 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 = true;
+ ok &= setUpTrace();
+ 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)
+ cleanUpTrace();
+
+ return g_traceAborted ? 1 : 0;
+}
diff --git a/cmds/bugreport/Android.mk b/cmds/bugreport/Android.mk
new file mode 100644
index 0000000..f476f5e
--- /dev/null
+++ b/cmds/bugreport/Android.mk
@@ -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
index 0000000..4a0b511
--- /dev/null
+++ b/cmds/bugreport/bugreport.c
@@ -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;
+}
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 55a36c2..9a332a9 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -101,8 +101,14 @@ static void dumpstate() {
run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
+ if (screenshot_path[0]) {
+ ALOGI("taking screenshot\n");
+ run_command(NULL, 10, "/system/bin/screencap", "-p", screenshot_path, NULL);
+ ALOGI("wrote screenshot: %s\n", screenshot_path);
+ }
+
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);
@@ -159,12 +165,6 @@ static void dumpstate() {
dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");
dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads");
- if (screenshot_path[0]) {
- ALOGI("taking screenshot\n");
- run_command(NULL, 5, SU_PATH, "root", "screenshot", screenshot_path, NULL);
- ALOGI("wrote screenshot: %s\n", screenshot_path);
- }
-
run_command("SYSTEM SETTINGS", 20, SU_PATH, "root", "sqlite3",
"/data/data/com.android.providers.settings/databases/settings.db",
"pragma user_version; select * from system; select * from secure; select * from global;", NULL);
@@ -196,13 +196,13 @@ static void dumpstate() {
property_get("dhcp.wlan0.gateway", network, "");
if (network[0])
- run_command("PING GATEWAY", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
+ run_command("PING GATEWAY", 10, "ping", "-c", "3", "-i", ".5", network, NULL);
property_get("dhcp.wlan0.dns1", network, "");
if (network[0])
- run_command("PING DNS1", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
+ run_command("PING DNS1", 10, "ping", "-c", "3", "-i", ".5", network, NULL);
property_get("dhcp.wlan0.dns2", network, "");
if (network[0])
- run_command("PING DNS2", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
+ run_command("PING DNS2", 10, "ping", "-c", "3", "-i", ".5", network, NULL);
#ifdef FWDUMP_bcmdhd
run_command("DUMP WIFI STATUS", 20,
SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", 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");
@@ -311,6 +311,7 @@ static void usage() {
" -b: play sound file instead of vibrate, at beginning of job\n"
" -e: play sound file instead of vibrate, at end of job\n"
" -q: disable vibrate\n"
+ " -B: send broadcast when finished (requires -o and -p)\n"
);
}
@@ -323,6 +324,7 @@ int main(int argc, char *argv[]) {
char* end_sound = 0;
int use_socket = 0;
int do_fb = 0;
+ int do_broadcast = 0;
if (getuid() != 0) {
// Old versions of the adb client would call the
@@ -348,7 +350,7 @@ int main(int argc, char *argv[]) {
dump_traces_path = dump_traces();
int c;
- while ((c = getopt(argc, argv, "b:de:ho:svqzp")) != -1) {
+ while ((c = getopt(argc, argv, "b:de:ho:svqzpB")) != -1) {
switch (c) {
case 'b': begin_sound = optarg; break;
case 'd': do_add_date = 1; break;
@@ -359,6 +361,7 @@ int main(int argc, char *argv[]) {
case 'q': do_vibrate = 0; break;
case 'z': do_compress = 6; break;
case 'p': do_fb = 1; break;
+ case 'B': do_broadcast = 1; break;
case '?': printf("\n");
case 'h':
usage();
@@ -474,6 +477,14 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "rename(%s, %s): %s\n", tmp_path, path, strerror(errno));
}
+ if (do_broadcast && use_outfile && do_fb) {
+ run_command(NULL, 5, "/system/bin/am", "broadcast", "--user", "0",
+ "-a", "android.intent.action.BUGREPORT_FINISHED",
+ "--es", "android.intent.extra.BUGREPORT", path,
+ "--es", "android.intent.extra.SCREENSHOT", screenshot_path,
+ "--receiver-permission", "android.permission.DUMP", NULL);
+ }
+
ALOGI("done\n");
return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 45247cd..67bbd7e 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -19,10 +19,14 @@
#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);
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 5b8ed49..fe716ac 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -51,7 +51,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;
@@ -76,23 +76,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;
@@ -103,7 +165,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);
@@ -316,7 +381,7 @@ pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level) {
chp = strchr(chp, '/');
if (chp) {
*chp = 0;
- mkdir(path, 0775); /* drwxrwxr-x */
+ mkdir(path, 0770); /* drwxrwx--- */
*chp++ = '/';
}
}
diff --git a/cmds/flatland/Android.mk b/cmds/flatland/Android.mk
new file mode 100644
index 0000000..5e57f02
--- /dev/null
+++ b/cmds/flatland/Android.mk
@@ -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
index 0000000..8365a31
--- /dev/null
+++ b/cmds/flatland/Composers.cpp
@@ -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
index 0000000..fd26ad3
--- /dev/null
+++ b/cmds/flatland/Flatland.h
@@ -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
index 0000000..4f7697f
--- /dev/null
+++ b/cmds/flatland/GLHelper.cpp
@@ -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 Surface(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
index 0000000..7a9e9e3
--- /dev/null
+++ b/cmds/flatland/GLHelper.h
@@ -0,0 +1,95 @@
+/*
+ * 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/Surface.h>
+#include <gui/SurfaceControl.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
index 0000000..99715d3
--- /dev/null
+++ b/cmds/flatland/Main.cpp
@@ -0,0 +1,716 @@
+/*
+ * 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/SurfaceControl.h>
+#include <gui/GLConsumer.h>
+#include <gui/Surface.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
index 0000000..ed47b3c
--- /dev/null
+++ b/cmds/flatland/README.txt
@@ -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
index 0000000..f1e5488
--- /dev/null
+++ b/cmds/flatland/Renderers.cpp
@@ -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
index 0000000..1dd4ee5
--- /dev/null
+++ b/cmds/installd/Android.mk
@@ -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
index 0000000..e544be7
--- /dev/null
+++ b/cmds/installd/commands.c
@@ -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 <sys/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, const char *seinfo)
+{
+ 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_setfilecon2(pkgdir, pkgname, seinfo, 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
index 0000000..230899b
--- /dev/null
+++ b/cmds/installd/installd.c
@@ -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 <sys/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]), arg[3]); /* pkgname, uid, gid, seinfo */
+}
+
+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", 4, 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
index 0000000..033d5a3
--- /dev/null
+++ b/cmds/installd/installd.h
@@ -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, const char *seinfo);
+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
index 0000000..c0192f4
--- /dev/null
+++ b/cmds/installd/tests/Android.mk
@@ -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
index 0000000..7cb9b37
--- /dev/null
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -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
index 0000000..625a35e
--- /dev/null
+++ b/cmds/installd/utils.c
@@ -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
index 0000000..de81889
--- /dev/null
+++ b/cmds/ip-up-vpn/Android.mk
@@ -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
index 0000000..9fcc950
--- /dev/null
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -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
index 0000000..b580390
--- /dev/null
+++ b/cmds/rawbu/Android.mk
@@ -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
index 0000000..c5b1efa
--- /dev/null
+++ b/cmds/rawbu/NOTICE
@@ -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
index 0000000..70e7b57
--- /dev/null
+++ b/cmds/rawbu/backup.cpp
@@ -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
index 0000000..73a8e22
--- /dev/null
+++ b/cmds/screenshot/Android.mk
@@ -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
index 0000000..cca80c3
--- /dev/null
+++ b/cmds/screenshot/screenshot.c
@@ -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
index 0000000..275bbb2
--- /dev/null
+++ b/cmds/service/Android.mk
@@ -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
index 0000000..e69de29
--- /dev/null
+++ b/cmds/service/MODULE_LICENSE_APACHE2
diff --git a/cmds/service/NOTICE b/cmds/service/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/cmds/service/NOTICE
@@ -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
index 0000000..32db83b
--- /dev/null
+++ b/cmds/service/service.cpp
@@ -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
index 0000000..8840867
--- /dev/null
+++ b/cmds/servicemanager/Android.mk
@@ -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
index 0000000..ff5aced
--- /dev/null
+++ b/cmds/servicemanager/bctest.c
@@ -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
index 0000000..1985756
--- /dev/null
+++ b/cmds/servicemanager/binder.c
@@ -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
index 0000000..d8c51ef
--- /dev/null
+++ b/cmds/servicemanager/binder.h
@@ -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
index 0000000..3eaf1eb
--- /dev/null
+++ b/cmds/servicemanager/service_manager.c
@@ -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;
+}
diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp
index 28e58e4..ce7fde0 100644
--- a/cmds/surfaceflinger/main_surfaceflinger.cpp
+++ b/cmds/surfaceflinger/main_surfaceflinger.cpp
@@ -20,9 +20,9 @@
using namespace android;
int main(int argc, char** argv) {
- SurfaceFlinger::publishAndJoinThreadPool(true);
// When SF is launched in its own process, limit the number of
// binder threads to 4.
ProcessState::self()->setThreadPoolMaxThreadCount(4);
+ SurfaceFlinger::publishAndJoinThreadPool(true);
return 0;
}